This notebook analyses both parts of the data in terms of variable importance, using a random forest model based on conditional inference trees and a conditional permutation variable importance algorithm.

Setup

Load packages

# load packages
library(tidyr)
library(ggplot2)
library(party)
library(conflicted)
library(tidyverse)
library(openxlsx)
library(caret)
library(viridis)
library(cowplot)
library(permimp)
# set package parameters
theme_set(theme_bw())

# plot colour scheme

mycolourlist = list(c(0, 102, 255), c(0, 204, 153), c(255, 0, 102), c(74, 111, 152), c(251, 164, 49), c(204, 153, 255), c(90, 192, 255), c(80, 245, 233), c(255, 90, 192), c(164, 201, 242), c(255, 254, 139), c(255, 243, 255))
mycolours = matrix()

for (ii in 1:length(mycolourlist)){
  mycolours[ii] = rgb(mycolourlist[[ii]][1]/255,
                      mycolourlist[[ii]][2]/255,
                      mycolourlist[[ii]][3]/255)
}

# toggle to save plots
saveplots = TRUE

if (saveplots){
  # set output plot directory
  choose.files(caption="Just cancel this", filters=matrix(data=c(" ", " "), ncol=2))  # workaround for bug in RTerm choose.dir
  outFigPath <- utils::choose.dir(caption="Select output folder to save plots '03 Experiment\\Experiment 1\\Analysis\\Plots'")
  
  if (!dir.exists(file.path(outFigPath, "svg"))){dir.create(file.path(outFigPath, "svg"))}
  if (!dir.exists(file.path(outFigPath, "pdf"))){dir.create(file.path(outFigPath, "pdf"))}
  
}

# toggle to save data
savedata = TRUE

if (savedata){
  # set output plot directory
  if (saveplots==FALSE){
    choose.files(caption="Just cancel this", filters=matrix(data=c(" ", " "), ncol=2))  # workaround for bug in RTerm choose.dir
  }
  outDataPath <- utils::choose.dir(caption="Select output folder to save data '03 Experiment\\Experiment 1\\Analysis\\R'")
}
 

Import data and wrangle


stimDatapath <- utils::choose.files(caption=r"(Select refmap_listest1_testdata_ByStim.csv from 03 Experiment\Experiment 1\Analysis\PostProcess)",
                                     filters=matrix(data=c("refmap_listest1_testdata_ByStim.csv", "refmap_listest1_testdata_ByStim.csv"), ncol=2))

stimData <- utils::read.csv(stimDatapath, header=TRUE)

colnames(stimData)[1] <- "Stimulus"

# make response proportions into percentages
stimData[['HighAnnoyPc']] <- stimData[['HighAnnoyProp']]*100
stimData[['dHighAnnoyPc']] <- stimData[['dHighAnnoyProp']]*100
# function to encode categorical to ordinal numeric variables
encode_ordinal <- function(x, order=unique(x)) {
  x <- as.numeric(factor(x, levels=order, exclude=NULL, order=TRUE))
  x
}

# definition of ordinal variable levels
SNRCats <- c("No UAS", "-16", "-10", "-4", "2", "8")
UASLAeqCats <- c("No UAS", "42", "48", "54", "60")

The aggregated data by stimulus are assigned to a dataframe, relevant categorical variables are converted to ordinal, and then the variable subset of interest is selected, NA rows dropped (ie, the ‘no UAS’ stimuli, as the conditional variable importance algorithm cannot currently handle NA values, which are present in all the UAS dB metrics), and a formula assigned.


stimDataNum <- data.frame()

stimDataNum <- cbind(stimData[, 'Stimulus'],
                     stimData[, "UASEvents"],
                     stimData[, which(colnames(stimData)=="UASLAeq"):
                                which(colnames(stimData)=="SNRlevel")],
                     stimData[, which(colnames(stimData)=="IntermitRatioC2MaxLR"):
                                which(colnames(stimData)=="IntermitRatioC5MaxLR")],
                     stimData[, which(colnames(stimData)=="UASLAEMaxLR"):
                                which(colnames(stimData)=="UASEPNLMaxLR")],
                     stimData[, which(colnames(stimData)=="UASLoudECMAPowAvgBin"):
                                which(colnames(stimData)=="UASLoudISO3PowAvgBin")],
                     stimData[, which(colnames(stimData)=="UASTonalECMAAvgMaxLR"):
                                which(colnames(stimData)=="UASSharpvBISO105ExBin")],
                     stimData[, which(colnames(stimData)=="UASImpulsSHMPowAvgMaxLR"):
                                which(colnames(stimData)=="UASPsychAnnoyBoucher")],
                     stimData[, which(colnames(stimData)=="LAeqLAF90diff"):
                                which(colnames(stimData)=="dPsychAnnoyBoucher")],
                     stimData[, which(colnames(stimData)=="ValenceMedian"):
                                which(colnames(stimData)=="dHighAnnoyProp")],
                     stimData[, which(colnames(stimData)=="HighAnnoyPc"):
                                which(colnames(stimData)=="dHighAnnoyPc")])

# remove duplicated variables
stimDataNum <- subset(stimDataNum, select = -c(UASLAeq))

colnames(stimDataNum)[1] <- "Stimulus"
colnames(stimDataNum)[2] <- "UASEvents"

# make the discrete ordinal outcome variables factors
stimDataNum[['UASEvents']] <- factor(stimDataNum[['UASEvents']], levels=c(0, 1, 3, 5, 9), order=TRUE)
stimDataNum[['ValenceMedian']] <- factor(stimDataNum[['ValenceMedian']], levels=c(1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5), order=TRUE)
stimDataNum[['ArousalMedian']] <- factor(stimDataNum[['ArousalMedian']], levels=c(1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5), order=TRUE)
stimDataNum[['AnnoyMedian']] <- factor(stimDataNum[['AnnoyMedian']], levels=c(0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5,
                                                                                5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10), order=TRUE)
stimDataNum[['dValenceMedian']] <- factor(stimDataNum[['dValenceMedian']], levels=c(-4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5, 0,
                                                                                      0.5, 1, 1.5, 2, 2.5, 3, 3.5,  4), order=TRUE)
stimDataNum[['dArousalMedian']] <- factor(stimDataNum[['dArousalMedian']], levels=c(-4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5, 0,
                                                                                      0.5, 1, 1.5, 2, 2.5, 3, 3.5,  4), order=TRUE)
stimDataNum[['dAnnoyMedian']] <- factor(stimDataNum[['dAnnoyMedian']], levels=c(-10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5,
                                                                                  -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5,
                                                                                 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5,
                                                                                 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10), order=TRUE)

# omit ambient-only stimuli
stimDataNum <- stimDataNum |> dplyr::filter(UASEvents != 0)


stimDataNum$SNRlevel <- as.numeric(stimDataNum$SNRlevel)

Random forest functions

Write a function to train a conditional-inference random forest (crf) model on input data according to input formula, iterate over input random seeds, average error and variable importance metrics, and output metrics with plotted

Averaging over multiple random seeds


multi_crfReg <- function(dataIn, iVars, dVar, seeds, ntree, mtry, permImpCondThres=0.95, minsplit=20, minbucket=7, nperm=1){
  # initialise variables
  crfOOBErrAll <- 0
  crfOOBRMSE <- 0
  crfOOBMAE <- 0
  crfOOBErrR2 <- 0
  crfMarPermImpVals <- 0
  crfConPermImpVals <- 0
  crfMarPermImpValsPerTree <- data.frame()
  crfConPermImpValsPerTree <- data.frame()
  
  for (iters in 1:length(seeds)){
    
    # formula for regression
    formVars <- reformulate(iVars, dVar)
    
    # set random seed
    set.seed(seeds[iters])
    # train crf model
    crfModel <- party::cforest(formVars, data=dataIn,
                               controls=party::cforest_unbiased(ntree=ntree,
                                                                mtry=mtry,
                                                                minsplit=minsplit,
                                                                minbucket=minbucket))
    
    # get OOB predictions
    crfModelOOB <- predict(crfModel, OOB=TRUE, type='response')
    
    # get OOB error
    crfModelOOBErr <- as.numeric(as.matrix(as.numeric(as.matrix(crfModelOOB))
                                           - as.numeric(as.matrix(crfModel@data@env$response[[names(crfModel@data@env$response)]]))))

    # OOB RMSE, MAE and Rsquared
    crfOOBRMSE <- crfOOBRMSE + sqrt(mean(crfModelOOBErr^2))
    crfOOBMAE <- crfOOBMAE + mean(abs(crfModelOOBErr))
    crfOOBErrR2 <- crfOOBErrR2 + cor(as.numeric(as.matrix(crfModelOOB)),
                                     as.numeric(as.matrix(crfModel@data@env$response[[names(crfModel@data@env$response)]])))^2

    # set random seed
    set.seed(seeds[iters])

    # set random seed
    set.seed(seeds[iters])
    # conditional variable permutation importance
    crfConPermImp <- permimp::permimp(crfModel, nperm=nperm, conditional=TRUE, threshold=permImpCondThres, progressBar=FALSE)
    
    crfConPermImpVals <- crfConPermImpVals + crfConPermImp$values
    crfConPermImpValsPerTree <- rbind(crfConPermImpValsPerTree, crfConPermImp$perTree)
  }
  
  # average metrics
  crfOOBErrAll <- crfOOBErrAll/length(seeds)
  crfOOBRMSE <- crfOOBRMSE/length(seeds)
  crfOOBMAE <- crfOOBMAE/length(seeds)
  crfOOBErrR2 <- crfOOBErrR2/length(seeds)
  crfConPermImpVals <- data.frame(CondPermImp=sort(crfConPermImpVals/length(seeds), decreasing=TRUE))
  crfConPermImpValsQtl <- data.frame(apply(crfConPermImpValsPerTree, 2, quantile, probs=c(0.25, 0.50, 0.75)))
  
  resultsOut <- list('OOB_RMSE'=crfOOBRMSE, 'OOB_MAE'=crfOOBMAE, 'Rsquared'=crfOOBErrR2, 'conditional_permimp'=crfConPermImpVals,                      'conditional_permimp_perTree'=crfConPermImpValsPerTree, 'conditional_permimp_qtl'=crfConPermImpValsQtl)
  return(resultsOut)
}

Comparing rankings from two seeds


crfReg <- function(dataIn, iVars, dVar, seeds, ntree, mtry, permImpCondThres=0.95, minsplit=20, minbucket=7, nperm=1){
  # initialise variables
  crfOOBErrAll <- 0
  crfOOBRMSE <- 0
  crfOOBMAE <- 0
  crfOOBErrR2 <- 0
  crfMarPermImpVals <- 0
  crfConPermImpVals <- 0
  crfMarPermImpValsPerTree <- data.frame()
  crfConPermImpValsPerTree <- data.frame()

  # formula for regression
  formVars <- reformulate(iVars, dVar)
  
  for (iters in 1:length(seeds)){
  
    # set random seed
    set.seed(seeds[iters])
    # train crf model
    crfModel <- party::cforest(formVars, data=dataIn,
                               controls=party::cforest_unbiased(ntree=ntree,
                                                                mtry=mtry,
                                                                minsplit=minsplit,
                                                                minbucket=minbucket))
    
    # conditional variable permutation importance
    crfConPermImp <- permimp::permimp(crfModel, nperm=nperm, conditional=TRUE, threshold=permImpCondThres, progressBar=FALSE)
    
    crfConPermImpVals <- crfConPermImp$values
    
    if (iters == 1){
      crfConPermImpVals1 <- data.frame(CondPermImp=sort(crfConPermImpVals, decreasing=TRUE))
      crfConPermImpValsPerTree1 <- crfConPermImp$perTree
      crfConPermImpValsQtl1 <- data.frame(apply(crfConPermImpValsPerTree1, 2, quantile, probs=c(0.25, 0.50, 0.75)))
      
      # get OOB predictions
      crfModelOOB <- predict(crfModel, OOB=TRUE, type='response')
      
      # get OOB error
      crfModelOOBErr <- as.numeric(as.matrix(as.numeric(as.matrix(crfModelOOB))
                                              - as.numeric(as.matrix(crfModel@data@env$response[[names(crfModel@data@env$response)]]))))
      
      # OOB RMSE, error quartiles and Rsquared
      crfOOBRMSE <- sqrt(mean(crfModelOOBErr^2))
      crfOOBMAE <- crfOOBMAE + mean(abs(crfModelOOBErr))
      crfOOBErrR2 <- cor(as.numeric(as.matrix(crfModelOOB)),
                                    as.numeric(as.matrix(crfModel@data@env$response[[names(crfModel@data@env$response)]])))^2

      }
    
    else{
      crfConPermImpValsN <- data.frame(CondPermImp=sort(crfConPermImpVals, decreasing=TRUE))
      
      nVarImpChecks <- min(max(sum(crfConPermImpVals1 >= crfConPermImpVals1$CondPermImp[1]*0.1),
                               sum(crfConPermImpValsN >= crfConPermImpValsN$CondPermImp[1]*0.1)), 4)  # number of variable importance values with a value at least 10% of the highest importance
      if (sum(rownames(crfConPermImpVals1)[1:nVarImpChecks] != rownames(crfConPermImpValsN)[1:nVarImpChecks]) > 0){
        warning("Permutation importance rank order within 10% of max differs between seeds: increase number of trees ('ntree') or number of permutations ('nperm'), or subsample of features ('mtry')")
      }
      else{
        resultsOut <- list('OOB_errors'=crfModelOOBErr, 'OOB_RMSE'=crfOOBRMSE, 'OOB_MAE'=crfOOBMAE, 'Rsquared'=crfOOBErrR2, 'conditional_permimp'=crfConPermImpVals1, 'conditional_permimp_perTree'=crfConPermImpValsPerTree1, 'conditional_permpimp_qtl'=crfConPermImpValsQtl1)
        return(resultsOut)
      }
      
    }
    
  }

}

Hyperparameter tuning

mtryTune <- function(dataIn, iVars, dVar, seeds, ntrees, minsplit=20, minbucket=7){

  formVars <- reformulate(iVars, dVar)
  
  # set mtry values and corresponding iVars/mtry ratios
  if (length(iVars) > 9){
    iVars_mtrys <- c(10.5, 5.25, 3.5, 2.75, 2.25, 1.75, 1.5, 1.25)
    mtrys <- as.integer(length(iVars)/iVars_mtrys)
  }
  else{
    mtrys <- seq(2, length(iVars) - 3, by=1)
    iVars_mtrys <- length(iVars)/mtrys
  }
  iVars_mtrys <- iVars_mtrys[mtrys >= 2]  # remove 0 or 1 values
  mtrys <- mtrys[mtrys >= 2]  # remove 0 or 1 values
  
  # remove any duplicated values
  iVars_mtrys <- iVars_mtrys[!(duplicated(mtrys) | duplicated(mtrys, fromLast = TRUE))]
  mtrys <- mtrys[!(duplicated(mtrys) | duplicated(mtrys, fromLast = TRUE))]

  # ensure mtrys is less than length(iVars)
  iVars_mtrys <- iVars_mtrys[mtrys < length(iVars)]
  mtrys <- mtrys[mtrys < length(iVars)]

  resRMSEMap = matrix(data=0, nrow=length(mtrys), ncol=length(ntrees))
  resRsquaredMap = matrix(data=0, nrow=length(mtrys), ncol=length(ntrees))
  resMAEMap = matrix(data=0, nrow=length(mtrys), ncol=length(ntrees))
  
  
  for (ii in seq(1, length(ntrees))){
    
    tuneMod.results <- data.frame(RMSE=numeric(length(mtrys)),
                                Rsquared=numeric(length(mtrys)),
                                MAE=numeric(length(mtrys)))
    
    for (seed in seeds){
      set.seed(seed)
      ntree = ntrees[ii]
      tuneMod <- caret::train(formVars,
                              data=dataIn,
                              method='cforest',
                              controls=party::cforest_unbiased(ntree=ntree,
                                                               minsplit=minsplit,
                                                               minbucket=minbucket),
                              tuneGrid=data.frame(.mtry=mtrys),
                              trControl = trainControl(method = "oob"))
      
      
      
      # accumulate results
      resRMSEMap[, ii] <- resRMSEMap[, ii] + tuneMod$results$RMSE
      resRsquaredMap[, ii] <- resRsquaredMap[, ii] + tuneMod$results$Rsquared
      resMAEMap[, ii] <- resMAEMap[, ii] + tuneMod$results$MAE
      
      tuneMod.results <- tuneMod.results + tuneMod$results[, which(names(tuneMod$results) != 'mtry')]
    }

    # average results
    tuneMod.results <- tuneMod.results/length(seeds)
    tuneMod.results <- cbind(tuneMod.results, data.frame(mtry=mtrys), data.frame(iVars_mtry=iVars_mtrys))

    print(tuneMod.results)

  }
  
  # average results
  resRMSEMap <- resRMSEMap/length(seeds)
  resRsquaredMap <- resRsquaredMap/length(seeds)
  resMAEMap <- resMAEMap/length(seeds)
  
  
  # convert to data frames with mtry as row names and ntree as column names, and convert to long format using tidyverse
  resdfRMSEMap <- as.data.frame(resRMSEMap)
  rownames(resdfRMSEMap) <- mtrys
  colnames(resdfRMSEMap) <- ntrees
  resdfRsquaredMap <- as.data.frame(resRsquaredMap)
  rownames(resdfRsquaredMap) <- mtrys
  colnames(resdfRsquaredMap) <- ntrees
  resdfMAEMap <- as.data.frame(resMAEMap)
  rownames(resdfMAEMap) <- mtrys
  colnames(resdfMAEMap) <- ntrees
  
  
  # convert dataframes to long format using tidyverse
  resdfRMSEMap <- resdfRMSEMap |>
                      rownames_to_column('mtry') |>
                          gather(key='ntree', value='RMSE', -mtry)
  
  resdfRsquaredMap <- resdfRsquaredMap |>
                          rownames_to_column('mtry') |>
                              gather(key='ntree', value='Rsquared', -mtry)
  
  resdfMAEMap <- resdfMAEMap |>
                    rownames_to_column('mtry') |>
                        gather(key='ntree', value='MAE', -mtry)
  
  # ensure ntree and mtry columns are ordered factors
  resdfRMSEMap$ntree <- factor(resdfRMSEMap$ntree, levels=as.character(ntrees))
  resdfRMSEMap$mtry <- factor(resdfRMSEMap$mtry, levels=as.character(mtrys))
  
  resdfRsquaredMap$ntree <- factor(resdfRsquaredMap$ntree, levels=as.character(ntrees))
  resdfRsquaredMap$mtry <- factor(resdfRsquaredMap$mtry, levels=as.character(mtrys))
  
  resdfMAEMap$ntree <- factor(resdfMAEMap$ntree, levels=as.character(ntrees))
  resdfMAEMap$mtry <- factor(resdfMAEMap$mtry, levels=as.character(mtrys))
  
  # plot heatmaps using ggplot, with extreme (min or max) value plotted as overlaid point using annotate and colourbar scale reversed
  pHeatmapRMSE <- ggplot(resdfRMSEMap) +
                    geom_tile(aes(x=ntree, y=mtry, fill=RMSE)) +
                        scale_fill_viridis(option="viridis", direction=-1) +
                          geom_point(data=resdfRMSEMap[which(resdfRMSEMap$RMSE
                                                             == min(resdfRMSEMap$RMSE),
                                                             arr.ind = TRUE),],
                                     aes(x=ntree, y=mtry), colour="red", size=2) +
                            guides(colour = guide_colourbar(reverse=TRUE)) +
                              labs(x="ntree", y="mtry", fill="RMSE") +
                                theme(text = element_text(family = "serif"))
  
  pHeatmapRsquared <- ggplot(resdfRsquaredMap) +
                        geom_tile(aes(x=ntree, y=mtry, fill=Rsquared)) +
                            scale_fill_viridis(option="viridis", direction=1) +
                              geom_point(data=resdfRsquaredMap[which(resdfRsquaredMap$Rsquared
                                                                     == max(resdfRsquaredMap$Rsquared),
                                                                     arr.ind = TRUE),],
                                         aes(x=ntree, y=mtry), colour="red", size=2) +
                                guides(colour = guide_colourbar(reverse=TRUE)) +
                                  labs(x="ntree", y="mtry", fill="Rsquared") +
                                    theme(text = element_text(family = "serif"))
  
  pHeatmapMAE <- ggplot(resdfMAEMap) +
                    geom_tile(aes(x=ntree, y=mtry, fill=MAE)) +
                        scale_fill_viridis(option="viridis", direction=-1) +
                          geom_point(data=resdfMAEMap[which(resdfMAEMap$MAE
                                                            == min(resdfMAEMap$MAE),
                                                            arr.ind = TRUE),],
                                     aes(x=ntree, y=mtry), colour="red", size=2) +
                            guides(colour = guide_colourbar(reverse=TRUE)) +
                              labs(x="ntree", y="mtry", fill="MAE") +
                                theme(text = element_text(family = "serif"))
  
  p <-  cowplot::plot_grid(pHeatmapRMSE,
                           pHeatmapRsquared,
                           pHeatmapMAE,
                           ncol=3, nrow=1)
  
  return(p)

}  # end of function

Parts A & B analysis

Set global parameters


permImpCondThres <- 0.95
minsplit <- 20
minbucket <- 7
ntrees <- c(251, 501, 1001, 1501, 2501, 4001, 5501)

eventVar <- "UASEvents"
ambVar <- "AmbientLAeq"

Mean change in annoyance

Initialise results output variables

resdAnnoyMnFitAB <- data.frame(RMSE = numeric(),
                               MAE = numeric(),
                               Rsquared = numeric())
resdAnnoyMnPermImpAB <- list()

Absolute variables

Set variables


iVars <- names(stimDataNum)[which(names(stimDataNum) == 'UASEvents'):which(names(stimDataNum) == 'UASPsychAnnoyBoucher')]
iVars <- iVars[! iVars %in% c('SNRlevel', 'IntermitRatioC2MaxLR', 'IntermitRatioC3MaxLR', 'IntermitRatioC5MaxLR')]

dVar <- "dAnnoyMean"

seeds <- c(578312, 544, 84894, 54654, 153157)

Hyperparameter tuning


p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
              ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAbsVarsHyperTune.svg", width=12, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAbsVarsHyperTune.svg")

  ggsave(filename="PtsABdAnnoyMnAbsVarsHyperTune.pdf", width=12, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAbsVarsHyperTune.pdf")
}

Selected hyperparameters


ntree <- 2501
mtry <- as.integer(length(iVars)/1.75)

Run model

Train preliminary model


nperm <- 5

resultsOutAbs <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbs$OOB_RMSE
[1] 0.7179463
resultsOutAbs$OOB_MAE
[1] 0.5697517
resultsOutAbs$Rsquared
[1] 0.81913

Train multiple seeds model


resultsOutAbs <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbs$OOB_RMSE
[1] 0.7262005
resultsOutAbs$OOB_MAE
[1] 0.5771998
resultsOutAbs$Rsquared
[1] 0.8140447

# store results
resdAnnoyMnFitAB['Abs vars', 'RMSE'] <- resultsOutAbs$OOB_RMSE
resdAnnoyMnFitAB['Abs vars', 'MAE'] <- resultsOutAbs$OOB_MAE
resdAnnoyMnFitAB['Abs vars', 'Rsquared'] <- resultsOutAbs$Rsquared
resdAnnoyMnPermImpAB$AbsVars <- resultsOutAbs$conditional_permimp

Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutAbs.conimp <- arrange(resultsOutAbs$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutAbs.conimp) + geom_col(aes(x=factor(rownames(resultsOutAbs.conimp), levels=rownames(resultsOutAbs.conimp)), y=CondPermImp), fill=mycolours[1], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) +
  coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAbsVarsConPermimp.svg", width=8, height=13, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAbsVarsConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAbsVarsConPermimp.pdf", width=8, height=13, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAbsVarsConPermimp.pdf")
}

# Plot only positive values

resultsOutAbs.conimpPtv <- resultsOutAbs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > 0)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbs.conimpPtv) + geom_col(aes(x=factor(rownames(resultsOutAbs.conimpPtv), levels=rownames(resultsOutAbs.conimpPtv)), y=CondPermImp), fill=mycolours[1], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAbsVarsConPermimpPtv.svg", width=8, height=10, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAbsVarsConPermimpPtv.svg")
  
  ggsave(filename="PtsABdAnnoyMnAbsVarsConPermimpPtv.pdf", width=8, height=10, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAbsVarsConPermimpPtv.pdf")
}

# Plot only values within 1% of the maximum

resultsOutAbs.conimp1pc <- resultsOutAbs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > max(resultsOutAbs.conimp)/100)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbs.conimp1pc) + geom_col(aes(x=factor(rownames(resultsOutAbs.conimp1pc), levels=rownames(resultsOutAbs.conimp1pc)), y=CondPermImp), fill=mycolours[1], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAbsVarsConPermimp1pc.svg", width=8, height=3, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAbsVarsConPermimp1pc.svg")
  
  ggsave(filename="PtsABdAnnoyMnAbsVarsConPermimp1pc.pdf", width=8, height=3, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAbsVarsConPermimp1pc.pdf")
}

Selected metric


absVar <- "UASLAEMaxLR"

SQM analysis

Individual SQMs

Sharpness
Set variables

iVars <- c(absVar, eventVar, ambVar, "UASSharpAurISO3PowAvgBin", "UASSharpAurISO305ExBin", "UASSharpAurSHMPowAvgBin", "UASSharpAurSHM05ExBin", "UASSharpAurISO1PowAvgBin", "UASSharpAurISO105ExBin", "UASSharpvBISO1PowAvgBin", "UASSharpvBISO105ExBin", "UASSharpDINPowAvgBin", "UASSharpDIN05ExBin", "UASSharpAurISO1MedBin",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dAnnoyMean"

seeds <- c(7041, 905, 4984651, 6513213, 120651)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/1.5)
Run model

Train preliminary model


nperm <- 5

resultsOutSharp <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 0.6520071
resultsOutSharp$OOB_MAE
[1] 0.5246782
resultsOutSharp$Rsquared
[1] 0.8553724

Train multiple seeds model


resultsOutSharp <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 0.6548829
resultsOutSharp$OOB_MAE
[1] 0.5229297
resultsOutSharp$Rsquared
[1] 0.8539235
# store results
resdAnnoyMnFitAB['Abs sharp', 'RMSE'] <- resultsOutSharp$OOB_RMSE
resdAnnoyMnFitAB['Abs sharp', 'MAE'] <- resultsOutSharp$OOB_MAE
resdAnnoyMnFitAB['Abs sharp', 'Rsquared'] <- resultsOutSharp$Rsquared
resdAnnoyMnPermImpAB$AbsSharp <- resultsOutSharp$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSharp.conimp <- arrange(resultsOutSharp$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSharp.conimp) + geom_col(aes(x=factor(rownames(resultsOutSharp.conimp), levels=rownames(resultsOutSharp.conimp)), y=CondPermImp), fill=mycolours[2], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("Sharpness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnSharpConPermimp.svg", width=8, height=4.9, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnSharpConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnSharpConPermimp.pdf", width=8, height=4.9, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnSharpConPermimp.pdf")
}

Selected metric


sharpVar <- "UASSharpAurISO305ExBin"
Tonal loudness and tonality
Set variables

iVars <- c(absVar, eventVar, ambVar, "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR", "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR",     "UASTonalAwSHMInt05ExMaxLR", "UASTonLdECMAPowAvgBin", "UASTonLdECMA05ExBin", "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dAnnoyMean"

seeds <- c(540, 104798, 456464, 87331, 94564)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/1.5)
Run model

Train preliminary model

# Tonality with tonal loudness

nperm <- 5

resultsOutTonal1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 0.6817226
resultsOutTonal1$OOB_MAE
[1] 0.5354283
resultsOutTonal1$Rsquared
[1] 0.8328264

Train multiple seeds model

# Tonality with tonal loudness

resultsOutTonal1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 0.6726346
resultsOutTonal1$OOB_MAE
[1] 0.5320356
resultsOutTonal1$Rsquared
[1] 0.8402319
# store results
resdAnnoyMnFitAB['Abs tonal inc loud', 'RMSE'] <- resultsOutTonal1$OOB_RMSE
resdAnnoyMnFitAB['Abs tonal inc loud', 'MAE'] <- resultsOutTonal1$OOB_MAE
resdAnnoyMnFitAB['Abs tonal inc loud', 'Rsquared'] <- resultsOutTonal1$Rsquared
resdAnnoyMnPermImpAB$AbsTonal1 <- resultsOutTonal1$conditional_permimp
Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal1.conimp <- arrange(resultsOutTonal1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal1.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal1.conimp), levels=rownames(resultsOutTonal1.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("Tonality inc. tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 1.4))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnTonalLdConPermimp.svg", width=8, height=5, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnTonalLdConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnTonalLdConPermimp.pdf", width=8, height=5, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnTonalLdConPermimp.pdf")
}

Selected metric


tonLdVar <- "UASTonLdECMAPowAvgBin"
Tonality without tonal loudness or tonal sharpness
Set variables

iVars <- c(absVar, eventVar, ambVar, "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR", "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR", "UASTonalAwSHMInt05ExMaxLR",    "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR")
dVar <- "dAnnoyMean"

seeds <- c(156089, 5860, 10528, 89541, 4685146)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 501
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model

# Tonality

nperm <- 5

resultsOutTonal2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 0.6735604
resultsOutTonal2$OOB_MAE
[1] 0.5215824
resultsOutTonal2$Rsquared
[1] 0.835001

Train multiple seeds model

# Tonality

resultsOutTonal2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 0.6770415
resultsOutTonal2$OOB_MAE
[1] 0.526503
resultsOutTonal2$Rsquared
[1] 0.8322548

# store results
resdAnnoyMnFitAB['Abs tonal no loud', 'RMSE'] <- resultsOutTonal2$OOB_RMSE
resdAnnoyMnFitAB['Abs tonal no loud', 'MAE'] <- resultsOutTonal2$OOB_MAE
resdAnnoyMnFitAB['Abs tonal no loud', 'Rsquared'] <- resultsOutTonal2$Rsquared
resdAnnoyMnPermImpAB$AbsTonal2 <- resultsOutTonal2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal2.conimp <- arrange(resultsOutTonal2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal2.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal2.conimp), levels=rownames(resultsOutTonal2.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("Tonality w/o tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 1.4))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnTonalConPermimp.svg", width=8, height=4.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnTonalConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnTonalConPermimp.pdf", width=8, height=4.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnTonalConPermimp.pdf")
}

Selected metric


tonalVar <- "UASTonalAwSHMInt05ExMaxLR"
Fluctuation strength
Set variables

# Fluctuation strength
iVars <- c(absVar, eventVar, ambVar, "UASFluctOldSHM10ExBin", "UASFluctOldSHM05ExBin", "UASFluctECMA10ExBin", "UASFluctECMA05ExBin", "UASFluctFZ10ExMaxLR", "UASFluctFZ05ExMaxLR", "UASFluctOV10ExMaxLR", "UASFluctOV05ExMaxLR")
dVar <- "dAnnoyMean"

seeds <- c(25107, 546098, 195, 5937, 102658)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 5501
mtry <- as.integer(length(iVars)/1.5)
Run model

Train preliminary model


nperm <- 5

resultsOutFluct <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 0.6411068
resultsOutFluct$OOB_MAE
[1] 0.5139778
resultsOutFluct$Rsquared
[1] 0.8590107

Train multiple seeds model


resultsOutFluct <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 0.6442208
resultsOutFluct$OOB_MAE
[1] 0.5160342
resultsOutFluct$Rsquared
[1] 0.8570714

# store results
resdAnnoyMnFitAB['Abs fluct', 'RMSE'] <- resultsOutFluct$OOB_RMSE
resdAnnoyMnFitAB['Abs fluct', 'MAE'] <- resultsOutFluct$OOB_MAE
resdAnnoyMnFitAB['Abs fluct', 'Rsquared'] <- resultsOutFluct$Rsquared
resdAnnoyMnPermImpAB$AbsFluct <- resultsOutFluct$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutFluct.conimp <- arrange(resultsOutFluct$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutFluct.conimp) + geom_col(aes(x=factor(rownames(resultsOutFluct.conimp), levels=rownames(resultsOutFluct.conimp)), y=CondPermImp), fill=mycolours[4], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("Fluctuation strength") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnFluctConPermimp.svg", width=8, height=3.5, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnFluctConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnFluctConPermimp.pdf", width=8, height=3.5, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnFluctConPermimp.pdf")
}

Selected metric


fluctVar <- "UASFluctECMA10ExBin"
Roughness
Set variables

# Roughness
iVars <- c(absVar, eventVar, ambVar, "UASRoughECMA10ExBin", "UASRoughECMA05ExBin", "UASRoughFZ10ExMaxLR", "UASRoughFZ05ExMaxLR", "UASRoughDW10ExMaxLR", "UASRoughDW05ExMaxLR")
dVar <- "dAnnoyMean"

seeds <- c(4701, 52187, 16589, 65217, 16893)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1001
mtry <- as.integer(length(iVars)/1.8)
Run model

Train preliminary model


nperm <- 5

resultsOutRough <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 0.6741884
resultsOutRough$OOB_MAE
[1] 0.5361558
resultsOutRough$Rsquared
[1] 0.8539761

Train multiple seeds model


resultsOutRough <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 0.6614793
resultsOutRough$OOB_MAE
[1] 0.5271245
resultsOutRough$Rsquared
[1] 0.8617422
# store results
resdAnnoyMnFitAB['Abs rough', 'RMSE'] <- resultsOutRough$OOB_RMSE
resdAnnoyMnFitAB['Abs rough', 'MAE'] <- resultsOutRough$OOB_MAE
resdAnnoyMnFitAB['Abs rough', 'Rsquared'] <- resultsOutRough$Rsquared
resdAnnoyMnPermImpAB$AbsRough <- resultsOutRough$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutRough.conimp <- arrange(resultsOutRough$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutRough.conimp) + geom_col(aes(x=factor(rownames(resultsOutRough.conimp), levels=rownames(resultsOutRough.conimp)), y=CondPermImp), fill=mycolours[5], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("Roughness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnRoughConPermimp.svg", width=8, height=2.9, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnRoughConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnRoughConPermimp.pdf", width=8, height=2.9, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnRoughConPermimp.pdf")
}

Selected metric


roughVar <- "UASRoughFZ05ExMaxLR"
Impulsiveness
Set variables
# Impulsiveness
iVars <- c(absVar, eventVar, ambVar, "UASImpulsSHMAvgMaxLR", "UASImpulsSHM05ExMaxLR", "UASImpulsSHMPowAvgMaxLR", "UASImpulsLoudWZAvgMaxLR", "UASImpulsLoudWZ05ExMaxLR", "UASImpulsLoudWZPowAvgMaxLR", "UASImpulsLoudWECMAAvgBin", "UASImpulsLoudWECMA05ExBin", "UASImpulsLoudWECMAPowAvgBin")
dVar <- "dAnnoyMean"

seeds <- c(8495, 59867, 5416, 9843, 86)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 4001
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutImpuls <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 0.6721437
resultsOutImpuls$OOB_MAE
[1] 0.5263928
resultsOutImpuls$Rsquared
[1] 0.8394367

Train multiple seeds model


resultsOutImpuls <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 0.671666
resultsOutImpuls$OOB_MAE
[1] 0.525632
resultsOutImpuls$Rsquared
[1] 0.8396473

# store results
resdAnnoyMnFitAB['Abs impuls', 'RMSE'] <- resultsOutImpuls$OOB_RMSE
resdAnnoyMnFitAB['Abs impuls', 'MAE'] <- resultsOutImpuls$OOB_MAE
resdAnnoyMnFitAB['Abs impuls', 'Rsquared'] <- resultsOutImpuls$Rsquared
resdAnnoyMnPermImpAB$AbsImpuls <- resultsOutImpuls$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutImpuls.conimp <- arrange(resultsOutImpuls$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutImpuls.conimp) + geom_col(aes(x=factor(rownames(resultsOutImpuls.conimp), levels=rownames(resultsOutImpuls.conimp)), y=CondPermImp), fill=mycolours[6], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("Impulsiveness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnImpulsConPermimp.svg", width=8, height=3.8, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnImpulsConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnImpulsConPermimp.pdf", width=8, height=3.8, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnImpulsConPermimp.pdf")
}

Selected metric


impulsVar <- "UASImpulsLoudWZAvgMaxLR"

SQM and loudness comparison

Now the highest importance SQMs are ranked against each other, controlling for UAS loudness and ambient LAeq.

Include tonal loudness
Set variables

iVars <- c(absVar, eventVar, ambVar, sharpVar, tonLdVar, fluctVar, roughVar, impulsVar)
dVar <- "dAnnoyMean"

seeds <- c(70498, 4, 14986, 453, 864)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 2501
mtry <- as.integer(length(iVars)/1.6)
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 0.6690392
resultsOutSQMs1$OOB_MAE
[1] 0.5373742
resultsOutSQMs1$Rsquared
[1] 0.8465967

Train multiple seeds model


resultsOutSQMs1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 0.6651781
resultsOutSQMs1$OOB_MAE
[1] 0.5349845
resultsOutSQMs1$Rsquared
[1] 0.8487605

# store results
resdAnnoyMnFitAB['Abs SQMs inc tonal loud', 'RMSE'] <- resultsOutSQMs1$OOB_RMSE
resdAnnoyMnFitAB['Abs SQMs inc tonal loud', 'MAE'] <- resultsOutSQMs1$OOB_MAE
resdAnnoyMnFitAB['Abs SQMs inc tonal loud', 'Rsquared'] <- resultsOutSQMs1$Rsquared
resdAnnoyMnPermImpAB$AbsSQMs1 <- resultsOutSQMs1$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs1.conimp <- arrange(resultsOutSQMs1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs1.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs1.conimp), levels=rownames(resultsOutSQMs1.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 1.3))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAbsSQMsTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAbsSQMsTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAbsSQMsTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAbsSQMsTonLdConPermimp.pdf")
}
Exclude tonal loudness
Set variables

iVars <- c(absVar, eventVar, ambVar, sharpVar, tonalVar, fluctVar, roughVar, impulsVar)
dVar <- "dAnnoyMean"

seeds <- c(546, 57203, 270835, 60592, 8094)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1501
mtry <- as.integer(length(iVars)/1.6)
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 0.6741422
resultsOutSQMs2$OOB_MAE
[1] 0.5413668
resultsOutSQMs2$Rsquared
[1] 0.8439303

Train multiple seeds model


resultsOutSQMs2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 0.6606138
resultsOutSQMs2$OOB_MAE
[1] 0.5318176
resultsOutSQMs2$Rsquared
[1] 0.8522687

# store results
resdAnnoyMnFitAB['Abs SQMs no tonal loud', 'RMSE'] <- resultsOutSQMs2$OOB_RMSE
resdAnnoyMnFitAB['Abs SQMs no tonal loud', 'MAE'] <- resultsOutSQMs2$OOB_MAE
resdAnnoyMnFitAB['Abs SQMs no tonal loud', 'Rsquared'] <- resultsOutSQMs2$Rsquared
resdAnnoyMnPermImpAB$AbsSQMs2 <- resultsOutSQMs2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs2.conimp <- arrange(resultsOutSQMs2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs2.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs2.conimp), levels=rownames(resultsOutSQMs2.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 1.3))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAbsSQMsNoTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAbsSQMsNoTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAbsSQMsNoTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAbsSQMsNoTonLdConPermimp.pdf")
}

Psychoacoustic annoyance metrics

Set variables

iVars <- c(ambVar, "UASPsychAnnoyWidmann", "UASPsychAnnoyMore", "UASPsychAnnoyDi", "UASPsychAnnoyTorija", "UASPsychAnnoyWillemsen", "UASPsychAnnoyBoucher")
dVar <- "dAnnoyMean"

seeds <- c(829, 9, 190, 4564, 924707824)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1501
mtry <- as.integer(length(iVars)/1.75)
Run model

Train preliminary model


nperm <- 5

resultsOutPA <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 0.6959625
resultsOutPA$OOB_MAE
[1] 0.5694917
resultsOutPA$Rsquared
[1] 0.8202226

Train multiple seeds model


resultsOutPA <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 0.6987601
resultsOutPA$OOB_MAE
[1] 0.5691674
resultsOutPA$Rsquared
[1] 0.8188134

# store results
resdAnnoyMnFitAB['Psychoacoustic annoyance', 'RMSE'] <- resultsOutPA$OOB_RMSE
resdAnnoyMnFitAB['Psychoacoustic annoyance', 'MAE'] <- resultsOutPA$OOB_MAE
resdAnnoyMnFitAB['Psychoacoustic annoyance', 'Rsquared'] <- resultsOutPA$Rsquared
resdAnnoyMnPermImpAB$AbsPA <- resultsOutPA$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutPA.conimp <- arrange(resultsOutPA$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutPA.conimp) + geom_col(aes(x=factor(rownames(resultsOutPA.conimp), levels=rownames(resultsOutPA.conimp)), y=CondPermImp), fill=mycolours[10], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 1.8))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAbsPAConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAbsPAConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAbsPAConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAbsPAConPermimp.pdf")
}

All variables (absolute and difference)

Set variables


iVars <- names(stimDataNum)[which(names(stimDataNum) == 'UASEvents'):which(names(stimDataNum) == 'dPsychAnnoyBoucher')]

dVar <- "dAnnoyMean"

seeds <- c(14569, 98651, 54654498, 454948, 41321)

Hyperparameter tuning


p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
              ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllVarsHyperTune.svg", width=12, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllVarsHyperTune.svg")

  ggsave(filename="PtsABdAnnoyMnAllVarsHyperTune.pdf", width=12, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllVarsHyperTune.pdf")
}

Selected hyperparameters


ntree <- 2501
mtry <- as.integer(length(iVars)/3.5)

Run model

Train preliminary model


nperm <- 5

resultsOutAbsDiffs <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbsDiffs$OOB_RMSE
[1] 0.5232516
resultsOutAbsDiffs$OOB_MAE
[1] 0.4044405
resultsOutAbsDiffs$Rsquared
[1] 0.8933325

Train multiple seeds model


resultsOutAbsDiffs <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbsDiffs$OOB_RMSE
[1] 0.5251036
resultsOutAbsDiffs$OOB_MAE
[1] 0.4057256
resultsOutAbsDiffs$Rsquared
[1] 0.8925424
# store results
resdAnnoyMnFitAB['All vars', 'RMSE'] <- resultsOutAbsDiffs$OOB_RMSE
resdAnnoyMnFitAB['All vars', 'MAE'] <- resultsOutAbsDiffs$OOB_MAE
resdAnnoyMnFitAB['All vars', 'Rsquared'] <- resultsOutAbsDiffs$Rsquared
resdAnnoyMnPermImpAB$AllVars <- resultsOutAbsDiffs$conditional_permimp

Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutAbsDiffs.conimp <- arrange(resultsOutAbsDiffs$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutAbsDiffs.conimp) + geom_col(aes(x=factor(rownames(resultsOutAbsDiffs.conimp), levels=rownames(resultsOutAbsDiffs.conimp)), y=CondPermImp), fill=mycolours[9], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllVarsConPermimp.svg", width=8, height=30, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllVarsConPermimp.svg")

  ggsave(filename="PtsABdAnnoyMnAllVarsConPermimp.pdf", width=8, height=30, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllVarsConPermimp.pdf")
}

# Plot only positive values

resultsOutAbsDiffs.conimpPtv <- resultsOutAbsDiffs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > 0)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbsDiffs.conimpPtv) + geom_col(aes(x=factor(rownames(resultsOutAbsDiffs.conimpPtv), levels=rownames(resultsOutAbsDiffs.conimpPtv)), y=CondPermImp), fill=mycolours[9], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllVarsConPermimpPtv.svg", width=8, height=18, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllVarsConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllVarsConPermimpPtv.pdf", width=8, height=18, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllVarsConPermimp.pdf")
}

# Plot only values within 1% of the maximum

resultsOutAbsDiffs.conimp1pc <- resultsOutAbsDiffs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > max(resultsOutAbsDiffs.conimp)/100)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbsDiffs.conimp1pc) + geom_col(aes(x=factor(rownames(resultsOutAbsDiffs.conimp1pc), levels=rownames(resultsOutAbsDiffs.conimp1pc)), y=CondPermImp), fill=mycolours[9], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllVarsConPermimp1pc.svg", width=8, height=6, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllVarsConPermimp1pc.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllVarsConPermimp1pc.pdf", width=8, height=6, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllVarsConPermimp1pc.pdf")
}

Selected metric


allVar <- "Detect0p1dBIntMaxLR"

dSQM analysis

Individual SQMs

dSharpness
Set variables

iVars <- c(allVar, eventVar, ambVar, "dSharpAurISO3PowAvgBin", "dSharpAurISO305ExBin", "dSharpAurSHMPowAvgBin", "dSharpAurSHM05ExBin", "dTonShpAurSHMPowAvgBin", "dTonShpAurSHM05ExBin", "PartTonShpAurSHMPowAvgBin",
           "PartTonShpAurSHM05ExBin", "UASSharpAurISO3PowAvgBin", "UASSharpAurISO305ExBin", "UASSharpAurSHMPowAvgBin", "UASSharpAurSHM05ExBin", "UASSharpAurISO1PowAvgBin", "UASSharpAurISO105ExBin", "UASSharpvBISO1PowAvgBin", "UASSharpvBISO105ExBin", "UASSharpDINPowAvgBin", "UASSharpDIN05ExBin", "UASSharpAurISO1MedBin",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dAnnoyMean"

seeds <- c(84194, 905, 64815, 928054, 625091)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <-251
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutSharp <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 0.5170055
resultsOutSharp$OOB_MAE
[1] 0.3896635
resultsOutSharp$Rsquared
[1] 0.8955904

Train multiple seeds model


resultsOutSharp <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 0.5117732
resultsOutSharp$OOB_MAE
[1] 0.3910043
resultsOutSharp$Rsquared
[1] 0.8983711

# store results
resdAnnoyMnFitAB['All sharp', 'RMSE'] <- resultsOutSharp$OOB_RMSE
resdAnnoyMnFitAB['All sharp', 'MAE'] <- resultsOutSharp$OOB_MAE
resdAnnoyMnFitAB['All sharp', 'Rsquared'] <- resultsOutSharp$Rsquared
resdAnnoyMnPermImpAB$AllSharp <- resultsOutSharp$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSharp.conimp <- arrange(resultsOutSharp$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSharp.conimp) + geom_col(aes(x=factor(rownames(resultsOutSharp.conimp), levels=rownames(resultsOutSharp.conimp)), y=CondPermImp), fill=mycolours[2], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("All sharpness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllSharpConPermimp.svg", width=8, height=5.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllSharpConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllSharpConPermimp.pdf", width=8, height=5.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllSharpConPermimp.pdf")
}

Selected metric


allSharpVar <- "dSharpAurISO3PowAvgBin"
dTonal loudness and dtonality
Set variables

iVars <- c(allVar, eventVar, ambVar, "dTonalECMAAvgMaxLR", "dTonalSHMInt05ExMaxLR", "dTonalSHMIntAvgMaxLR", "dTonalECMA05ExMaxLR", "dTonalAwSHMAvgMaxLR",   "dTonalAwSHM05ExMaxLR", "dTonalAwSHMIntAvgMaxLR",   "dTonalAwSHMInt05ExMaxLR", "dTonLdECMAPowAvgBin", "dTonLdECMA05ExBin", "dTonShpAurSHMPowAvgBin",
           "dTonShpAurSHM05ExBin", "PartTonLdSHMPowAvgBin", "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR",  "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR",     "UASTonalAwSHMInt05ExMaxLR", "UASTonLdECMAPowAvgBin", "UASTonLdECMA05ExBin", "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dAnnoyMean"

seeds <- c(561684, 104798, 1536, 48, 48561)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model

# Tonality with tonal loudness

nperm <- 5

resultsOutTonal1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 0.5151597
resultsOutTonal1$OOB_MAE
[1] 0.4039491
resultsOutTonal1$Rsquared
[1] 0.8960478

Train multiple seeds model

# Tonality with tonal loudness

resultsOutTonal1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 0.5103953
resultsOutTonal1$OOB_MAE
[1] 0.4021889
resultsOutTonal1$Rsquared
[1] 0.8981797
# store results
resdAnnoyMnFitAB['All tonal inc loud', 'RMSE'] <- resultsOutTonal1$OOB_RMSE
resdAnnoyMnFitAB['All tonal inc loud', 'MAE'] <- resultsOutTonal1$OOB_MAE
resdAnnoyMnFitAB['All tonal inc loud', 'Rsquared'] <- resultsOutTonal1$Rsquared
resdAnnoyMnPermImpAB$AllTonal1 <- resultsOutTonal1$conditional_permimp
Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal1.conimp <- arrange(resultsOutTonal1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal1.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal1.conimp), levels=rownames(resultsOutTonal1.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("All tonality inc. tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 2.2))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllTonalLdConPermimp.svg", width=8, height=6, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllTonalLdConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllTonalLdConPermimp.pdf", width=8, height=6, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllTonalLdConPermimp.pdf")
}

Selected metric


allTonLdVar <- "dTonLdECMAPowAvgBin"
dTonality without dtonal loudness
Set variables

iVars <- c(allVar, eventVar, ambVar, "dTonalECMAAvgMaxLR", "dTonalSHMInt05ExMaxLR", "dTonalSHMIntAvgMaxLR", "dTonalECMA05ExMaxLR", "dTonalAwSHMAvgMaxLR",   "dTonalAwSHM05ExMaxLR", "dTonalAwSHMIntAvgMaxLR",   "dTonalAwSHMInt05ExMaxLR", "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR",   "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR", "UASTonalAwSHMInt05ExMaxLR",    "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR")
dVar <- "dAnnoyMean"

seeds <- c(410865, 2954, 70812, 203, 7984)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 501
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model

# Tonality

nperm <- 5

resultsOutTonal2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
                           ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 0.5010944
resultsOutTonal2$OOB_MAE
[1] 0.3841466
resultsOutTonal2$Rsquared
[1] 0.9029128

Train multiple seeds model

# Tonality

resultsOutTonal2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 0.5000554
resultsOutTonal2$OOB_MAE
[1] 0.3831858
resultsOutTonal2$Rsquared
[1] 0.9033995

# store results
resdAnnoyMnFitAB['All tonal no loud', 'RMSE'] <- resultsOutTonal2$OOB_RMSE
resdAnnoyMnFitAB['All tonal no loud', 'MAE'] <- resultsOutTonal2$OOB_MAE
resdAnnoyMnFitAB['All tonal no loud', 'Rsquared'] <- resultsOutTonal2$Rsquared
resdAnnoyMnPermImpAB$AllTonal2 <- resultsOutTonal2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal2.conimp <- arrange(resultsOutTonal2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal2.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal2.conimp), levels=rownames(resultsOutTonal2.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("All tonality w/o tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 2.6))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllTonalConPermimp.svg", width=8, height=5.8, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllTonalConPermimp.svg")
  
  ggsave(filename="PtsABAllAnnoyMndTonalConPermimp.pdf", width=8, height=5.8, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllTonalConPermimp.pdf")
}

Selected metric


allTonalVar <- "dTonalSHMIntAvgMaxLR"
dFluctuation strength
Set variables

# Fluctuation strength
iVars <- c(allVar, eventVar, ambVar, "dFluctECMA10ExBin", "dFluctECMA05ExBin", "dFluctOV10ExMaxLR", "dFluctOV05ExMaxLR", "UASFluctOldSHM10ExBin", "UASFluctOldSHM05ExBin", "UASFluctECMA10ExBin", "UASFluctECMA05ExBin", "UASFluctFZ10ExMaxLR", "UASFluctFZ05ExMaxLR", "UASFluctOV10ExMaxLR", "UASFluctOV05ExMaxLR")
dVar <- "dAnnoyMean"

seeds <- c(418657, 84, 1630, 18659, 3687)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 4001
mtry <- as.integer(length(iVars)/1.5)
Run model

Train preliminary model


nperm <- 5

resultsOutFluct <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 0.4966309
resultsOutFluct$OOB_MAE
[1] 0.3732706
resultsOutFluct$Rsquared
[1] 0.9046225

Train multiple seeds model


resultsOutFluct <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 0.4969742
resultsOutFluct$OOB_MAE
[1] 0.3744435
resultsOutFluct$Rsquared
[1] 0.9047033

# store results
resdAnnoyMnFitAB['All fluct', 'RMSE'] <- resultsOutFluct$OOB_RMSE
resdAnnoyMnFitAB['All fluct', 'MAE'] <- resultsOutFluct$OOB_MAE
resdAnnoyMnFitAB['All fluct', 'Rsquared'] <- resultsOutFluct$Rsquared
resdAnnoyMnPermImpAB$AllFluct <- resultsOutFluct$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutFluct.conimp <- arrange(resultsOutFluct$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutFluct.conimp) + geom_col(aes(x=factor(rownames(resultsOutFluct.conimp), levels=rownames(resultsOutFluct.conimp)), y=CondPermImp), fill=mycolours[4], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("All fluctuation strength") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllFluctConPermimp.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllFluctConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllFluctConPermimp.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllFluctConPermimp.pdf")
}

Selected metric


allFluctVar <- "dFluctECMA10ExBin"
dRoughness
Set variables

# Roughness
iVars <- c(allVar, eventVar, ambVar, "dRoughECMA10ExBin", "dRoughECMA05ExBin", "dRoughFZ10ExMaxLR", "dRoughFZ05ExMaxLR", "UASRoughECMA10ExBin", "UASRoughECMA05ExBin", "UASRoughFZ10ExMaxLR", "UASRoughFZ05ExMaxLR", "UASRoughDW10ExMaxLR", "UASRoughDW05ExMaxLR")
dVar <- "dAnnoyMean"

seeds <- c(69851, 85109, 410986, 1563, 896)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1001
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutRough <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 0.5060503
resultsOutRough$OOB_MAE
[1] 0.3812809
resultsOutRough$Rsquared
[1] 0.9036606

Train multiple seeds model


resultsOutRough <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 0.5099848
resultsOutRough$OOB_MAE
[1] 0.3855287
resultsOutRough$Rsquared
[1] 0.9015348
# store results
resdAnnoyMnFitAB['All rough', 'RMSE'] <- resultsOutRough$OOB_RMSE
resdAnnoyMnFitAB['All rough', 'MAE'] <- resultsOutRough$OOB_MAE
resdAnnoyMnFitAB['All rough', 'Rsquared'] <- resultsOutRough$Rsquared
resdAnnoyMnPermImpAB$AllRough <- resultsOutRough$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutRough.conimp <- arrange(resultsOutRough$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutRough.conimp) + geom_col(aes(x=factor(rownames(resultsOutRough.conimp), levels=rownames(resultsOutRough.conimp)), y=CondPermImp), fill=mycolours[5], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("All roughness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllRoughConPermimp.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllRoughConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllRoughConPermimp.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllRoughConPermimp.pdf")
}

Selected metric


allRoughVar <- "dRoughFZ05ExMaxLR"
dImpulsiveness
Set variables
# Impulsiveness
iVars <- c(allVar, eventVar, ambVar, "dImpulsSHMAvgMaxLR", "dImpulsSHM05ExMaxLR", "dImpulsSHMPowAvgMaxLR",
           "dImpulsLoudWZAvgMaxLR", "dImpulsLoudWZ05ExMaxLR", "dImpulsLoudWZPowAvgMaxLR",
           "dImpulsLoudWECMAAvgBin", "dImpulsLoudWECMA05ExBin", "dImpulsLoudWECMAPowAvgBin", "UASImpulsSHMAvgMaxLR", "UASImpulsSHM05ExMaxLR", "UASImpulsSHMPowAvgMaxLR", "UASImpulsLoudWZAvgMaxLR", "UASImpulsLoudWZ05ExMaxLR", "UASImpulsLoudWZPowAvgMaxLR", "UASImpulsLoudWECMAAvgBin", "UASImpulsLoudWECMA05ExBin", "UASImpulsLoudWECMAPowAvgBin")
dVar <- "dAnnoyMean"

seeds <- c(418659, 7805, 38475, 65834, 1653)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 501
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutImpuls <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 0.5081669
resultsOutImpuls$OOB_MAE
[1] 0.3888782
resultsOutImpuls$Rsquared
[1] 0.9001747

Train multiple seeds model


resultsOutImpuls <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 0.5077729
resultsOutImpuls$OOB_MAE
[1] 0.3904791
resultsOutImpuls$Rsquared
[1] 0.9006062

# store results
resdAnnoyMnFitAB['All impuls', 'RMSE'] <- resultsOutImpuls$OOB_RMSE
resdAnnoyMnFitAB['All impuls', 'MAE'] <- resultsOutImpuls$OOB_MAE
resdAnnoyMnFitAB['All impuls', 'Rsquared'] <- resultsOutImpuls$Rsquared
resdAnnoyMnPermImpAB$AllImpuls <- resultsOutImpuls$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutImpuls.conimp <- arrange(resultsOutImpuls$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutImpuls.conimp) + geom_col(aes(x=factor(rownames(resultsOutImpuls.conimp), levels=rownames(resultsOutImpuls.conimp)), y=CondPermImp), fill=mycolours[6], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + ggtitle("All impulsiveness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllImpulsConPermimp.svg", width=8, height=5.6, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllImpulsConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllImpulsConPermimp.pdf", width=8, height=5.6, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllImpulsConPermimp.pdf")
}

Selected metric


allImpulsVar <- "dImpulsLoudWZAvgMaxLR"

dSQM and loudness comparison

Now the highest importance dSQMs are ranked against each other, controlling for loudness difference.

Include dtonal loudness
Set variables

iVars <- c(allVar, eventVar, ambVar, allSharpVar, allTonLdVar, allFluctVar, allRoughVar, allImpulsVar)
dVar <- "dAnnoyMean"

seeds <- c(98465, 54163, 6541, 36485, 849675)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 501
mtry <- as.integer(length(iVars)/2)
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 0.4971808
resultsOutSQMs1$OOB_MAE
[1] 0.3860652
resultsOutSQMs1$Rsquared
[1] 0.9059535

Train multiple seeds model


resultsOutSQMs1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 0.4991241
resultsOutSQMs1$OOB_MAE
[1] 0.3861392
resultsOutSQMs1$Rsquared
[1] 0.904781

# store results
resdAnnoyMnFitAB['All SQMs inc tonal loud', 'RMSE'] <- resultsOutSQMs1$OOB_RMSE
resdAnnoyMnFitAB['All SQMs inc tonal loud', 'MAE'] <- resultsOutSQMs1$OOB_MAE
resdAnnoyMnFitAB['All SQMs inc tonal loud', 'Rsquared'] <- resultsOutSQMs1$Rsquared
resdAnnoyMnPermImpAB$AllSQMs1 <- resultsOutSQMs1$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs1.conimp <- arrange(resultsOutSQMs1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs1.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs1.conimp), levels=rownames(resultsOutSQMs1.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 2))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllSQMsTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllSQMsTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllSQMsTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllSQMsTonLdConPermimp.pdf")
}
Exclude tonal loudness
Set variables

iVars <- c(allVar, eventVar, ambVar, allSharpVar, allTonalVar, allFluctVar, allRoughVar, allImpulsVar)
dVar <- "dAnnoyMean"

seeds <- c(49865, 7852, 845961, 410583, 36748)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 2501
mtry <- as.integer(length(iVars)/1.6)
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 0.4906752
resultsOutSQMs2$OOB_MAE
[1] 0.3786122
resultsOutSQMs2$Rsquared
[1] 0.9089686

Train multiple seeds model


resultsOutSQMs2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 0.4897326
resultsOutSQMs2$OOB_MAE
[1] 0.376146
resultsOutSQMs2$Rsquared
[1] 0.9091913

# store results
resdAnnoyMnFitAB['All SQMs no tonal loud', 'RMSE'] <- resultsOutSQMs2$OOB_RMSE
resdAnnoyMnFitAB['All SQMs no tonal loud', 'MAE'] <- resultsOutSQMs2$OOB_MAE
resdAnnoyMnFitAB['All SQMs no tonal loud', 'Rsquared'] <- resultsOutSQMs2$Rsquared
resdAnnoyMnPermImpAB$AllSQMs2 <- resultsOutSQMs2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs2.conimp <- arrange(resultsOutSQMs2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs2.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs2.conimp), levels=rownames(resultsOutSQMs2.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 2))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllSQMsNoTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllSQMsNoTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllSQMsNoTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllSQMsNoTonLdConPermimp.pdf")
}

dPsychoacoustic annoyance metrics

Set variables

iVars <- c(ambVar, "dPsychAnnoyWidmann", "dPsychAnnoyMore", "dPsychAnnoyDi", "dPsychAnnoyTorija", "dPsychAnnoyWillemsen", "dPsychAnnoyBoucher", "UASPsychAnnoyWidmann", "UASPsychAnnoyMore", "UASPsychAnnoyDi", "UASPsychAnnoyTorija", "UASPsychAnnoyWillemsen", "UASPsychAnnoyBoucher")
dVar <- "dAnnoyMean"

seeds <- c(47896643, 475, 654, 98987132, 5446)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
              ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 4001
mtry <-  as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutPA <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 0.7066857
resultsOutPA$OOB_MAE
[1] 0.5544466
resultsOutPA$Rsquared
[1] 0.8031693

Train multiple seeds model


resultsOutPA <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 0.7071518
resultsOutPA$OOB_MAE
[1] 0.5548064
resultsOutPA$Rsquared
[1] 0.8028069

# store results
resdAnnoyMnFitAB['All Psychoacoustic annoyance', 'RMSE'] <- resultsOutPA$OOB_RMSE
resdAnnoyMnFitAB['All Psychoacoustic annoyance', 'MAE'] <- resultsOutPA$OOB_MAE
resdAnnoyMnFitAB['All Psychoacoustic annoyance', 'Rsquared'] <- resultsOutPA$Rsquared
resdAnnoyMnPermImpAB$AllPA <- resultsOutPA$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutPA.conimp <- arrange(resultsOutPA$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutPA.conimp) + geom_col(aes(x=factor(rownames(resultsOutPA.conimp), levels=rownames(resultsOutPA.conimp)), y=CondPermImp), fill=mycolours[10], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (mean change in annoyance)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 1.8))
pBar


if (saveplots){
  ggsave(filename="PtsABdAnnoyMnAllPAConPermimp.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdAnnoyMnAllPAConPermimp.svg")
  
  ggsave(filename="PtsABdAnnoyMnAllPAConPermimp.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdAnnoyMnAllPAConPermimp.pdf")
}

Save the results outputs to file


if (savedata){
  utils::write.csv(resdAnnoyMnFitAB, paste(outDataPath, "\\PtsABCRFdAnnoyMnOOBFit.csv", sep=""))
  ii <- 0
  temp = list()
  for (res in resdAnnoyMnPermImpAB){
    ii <- ii + 1
    temp[[ii]] <- as.data.frame(resdAnnoyMnPermImpAB[ii])
    names(temp[[ii]]) <- names(resdAnnoyMnPermImpAB[ii])
  }
  openxlsx::write.xlsx(temp, paste(outDataPath, "\\PtsABCRFdAnnoyMnConPermimp.xlsx",
                                   sep=""),
                       rowNames=TRUE)
}

(Change to) High annoyance

Initialise results output variables

resdHiAnnoyFitAB <- data.frame(RMSE = numeric(),
                             MAE = numeric(),
                             Rsquared = numeric())
resdHiAnnoyPermImpAB <- list()

Absolute variables

Set variables


iVars <- names(stimDataNum)[which(names(stimDataNum) == 'UASEvents'):which(names(stimDataNum) == 'UASPsychAnnoyBoucher')]
iVars <- iVars[! iVars %in% c('SNRlevel', 'IntermitRatioC2MaxLR', 'IntermitRatioC3MaxLR', 'IntermitRatioC5MaxLR')]
dVar <- "dHighAnnoyPc"

seeds <- c(578312, 544, 84894, 54654, 153157)

Hyperparameter tuning


p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAbsVarsHyperTune.svg", width=12, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAbsVarsHyperTune.svg")

  ggsave(filename="PtsABdHiAnnoyAbsVarsHyperTune.pdf", width=12, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAbsVarsHyperTune.pdf")
}

Selected hyperparameters


ntree <- 1501
mtry <- as.integer(length(iVars)/1.75)

Run model

Train preliminary model


nperm <- 5

resultsOutAbs <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbs$OOB_RMSE
[1] 6.203493
resultsOutAbs$OOB_MAE
[1] 4.787191
resultsOutAbs$Rsquared
[1] 0.6664964

Train multiple seeds model


resultsOutAbs <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbs$OOB_RMSE
[1] 6.205791
resultsOutAbs$OOB_MAE
[1] 4.775969
resultsOutAbs$Rsquared
[1] 0.6664051

# store results
resdHiAnnoyFitAB['Abs vars', 'RMSE'] <- resultsOutAbs$OOB_RMSE
resdHiAnnoyFitAB['Abs vars', 'MAE'] <- resultsOutAbs$OOB_MAE
resdHiAnnoyFitAB['Abs vars', 'Rsquared'] <- resultsOutAbs$Rsquared
resdHiAnnoyPermImpAB$AbsVars <- resultsOutAbs$conditional_permimp

Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutAbs.conimp <- arrange(resultsOutAbs$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutAbs.conimp) + geom_col(aes(x=factor(rownames(resultsOutAbs.conimp), levels=rownames(resultsOutAbs.conimp)), y=CondPermImp), fill=mycolours[1], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) +
  coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAbsVarsConPermimp.svg", width=8, height=14, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAbsVarsConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAbsVarsConPermimp.pdf", width=8, height=14, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAbsVarsConPermimp.pdf")
}

# Plot only positive values
resultsOutAbs.conimpPtv <- resultsOutAbs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > 0)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbs.conimpPtv,) + geom_col(aes(x=factor(rownames(resultsOutAbs.conimpPtv), levels=rownames(resultsOutAbs.conimpPtv)), y=CondPermImp), fill=mycolours[1], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAbsVarsConPermimpPtv.svg", width=8, height=10, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAbsVarsConPermimpPtv.svg")
  
  ggsave(filename="PtsABdHiAnnoyAbsVarsConPermimpPtv.pdf", width=8, height=10, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAbsVarsConPermimpPtv.pdf")
}

# Plot only values within 1% of the maximum
resultsOutAbs.conimp1pc <- resultsOutAbs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > max(resultsOutAbs.conimp)/100)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbs.conimp1pc,) + geom_col(aes(x=factor(rownames(resultsOutAbs.conimp1pc), levels=rownames(resultsOutAbs.conimp1pc)), y=CondPermImp), fill=mycolours[1], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAbsVarsConPermimp1pc.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAbsVarsConPermimp1pc.svg")
  
  ggsave(filename="PtsABdHiAnnoyAbsVarsConPermimp1pc.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAbsVarsConPermimp1pc.pdf")
}

Selected metric


absVar <- "UASLoudECMAPowAvgBin"

SQM analysis

Individual SQMs

Sharpness
Set variables

iVars <- c(absVar, eventVar, ambVar, "UASSharpAurISO3PowAvgBin", "UASSharpAurISO305ExBin", "UASSharpAurSHMPowAvgBin", "UASSharpAurSHM05ExBin", "UASSharpAurISO1PowAvgBin", "UASSharpAurISO105ExBin", "UASSharpvBISO1PowAvgBin", "UASSharpvBISO105ExBin", "UASSharpDINPowAvgBin", "UASSharpDIN05ExBin", "UASSharpAurISO1MedBin",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dHighAnnoyPc"

seeds <- c(7041, 905, 4984651, 6513213, 120651)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

NA
NA

Selected hyperparameters


ntree <- 1501
mtry <- as.integer(length(iVars)/2.25)
Run model

Train preliminary model


nperm <- 5

resultsOutSharp <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 5.965448
resultsOutSharp$OOB_MAE
[1] 4.532257
resultsOutSharp$Rsquared
[1] 0.6957333

Train multiple seeds model


resultsOutSharp <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 5.945878
resultsOutSharp$OOB_MAE
[1] 4.51012
resultsOutSharp$Rsquared
[1] 0.6984185

# store results
resdHiAnnoyFitAB['Abs sharp', 'RMSE'] <- resultsOutSharp$OOB_RMSE
resdHiAnnoyFitAB['Abs sharp', 'MAE'] <- resultsOutSharp$OOB_MAE
resdHiAnnoyFitAB['Abs sharp', 'Rsquared'] <- resultsOutSharp$Rsquared
resdHiAnnoyPermImpAB$AbsSharp <- resultsOutSharp$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSharp.conimp <- arrange(resultsOutSharp$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSharp.conimp) + geom_col(aes(x=factor(rownames(resultsOutSharp.conimp), levels=rownames(resultsOutSharp.conimp)), y=CondPermImp), fill=mycolours[2], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("Sharpness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoySharpConPermimp.svg", width=8, height=4.9, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoySharpConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoySharpConPermimp.pdf", width=8, height=4.9, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoySharpConPermimp.pdf")
}

Selected metric


sharpVar <- "UASSharpAurISO3PowAvgBin"
Tonal loudness and tonality
Set variables

iVars <- c(absVar, eventVar, ambVar, "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR", "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR",     "UASTonalAwSHMInt05ExMaxLR", "UASTonLdECMAPowAvgBin", "UASTonLdECMA05ExBin", "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dHighAnnoyPc"

seeds <- c(540, 104798, 456464, 87331, 94564)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/1.5)
Run model

Train preliminary model

# Tonality with tonal loudness

nperm <- 5

resultsOutTonal1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 6.358715
resultsOutTonal1$OOB_MAE
[1] 4.808657
resultsOutTonal1$Rsquared
[1] 0.6484513

Train multiple seeds model

# Tonality with tonal loudness

resultsOutTonal1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 6.320428
resultsOutTonal1$OOB_MAE
[1] 4.782126
resultsOutTonal1$Rsquared
[1] 0.6527379
# store results
resdHiAnnoyFitAB['Abs tonal inc loud', 'RMSE'] <- resultsOutTonal1$OOB_RMSE
resdHiAnnoyFitAB['Abs tonal inc loud', 'MAE'] <- resultsOutTonal1$OOB_MAE
resdHiAnnoyFitAB['Abs tonal inc loud', 'Rsquared'] <- resultsOutTonal1$Rsquared
resdHiAnnoyPermImpAB$AbsTonal1 <- resultsOutTonal1$conditional_permimp
Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal1.conimp <- arrange(resultsOutTonal1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal1.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal1.conimp), levels=rownames(resultsOutTonal1.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("Tonality inc. tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 110))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyTonalLdConPermimp.svg", width=8, height=4.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyTonalLdConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyTonalLdConPermimp.pdf", width=8, height=4.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyTonalLdConPermimp.pdf")
}

Selected metric


tonLdVar <- "UASTonLdECMAPowAvgBin"
Tonality without tonal loudness
Set variables

iVars <- c(absVar, eventVar, ambVar, "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR", "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR", "UASTonalAwSHMInt05ExMaxLR",    "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR")
dVar <- "dHighAnnoyPc"

seeds <- c(156089, 5860, 10528, 89541, 4685146)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model

# Tonality

nperm <- 5

resultsOutTonal2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
                           ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm,
                           minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 6.434733
resultsOutTonal2$OOB_MAE
[1] 4.888113
resultsOutTonal2$Rsquared
[1] 0.640617

Train multiple seeds model

# Tonality

resultsOutTonal2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 6.454118
resultsOutTonal2$OOB_MAE
[1] 4.898967
resultsOutTonal2$Rsquared
[1] 0.6386236

# store results
resdHiAnnoyFitAB['Abs tonal no loud', 'RMSE'] <- resultsOutTonal2$OOB_RMSE
resdHiAnnoyFitAB['Abs tonal no loud', 'MAE'] <- resultsOutTonal2$OOB_MAE
resdHiAnnoyFitAB['Abs tonal no loud', 'Rsquared'] <- resultsOutTonal2$Rsquared
resdHiAnnoyPermImpAB$AbsTonal2 <- resultsOutTonal2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal2.conimp <- arrange(resultsOutTonal2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal2.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal2.conimp), levels=rownames(resultsOutTonal2.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("Tonality w/o tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 110))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyTonalConPermimp.svg", width=8, height=3.8, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyTonalConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyTonalConPermimp.pdf", width=8, height=3.8, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyTonalConPermimp.pdf")
}

Selected metric


tonalVar <- "UASTonalAwSHMInt05ExMaxLR"
Fluctuation strength
Set variables

# Fluctuation strength
iVars <- c(absVar, eventVar, ambVar, "UASFluctOldSHM10ExBin", "UASFluctOldSHM05ExBin", "UASFluctECMA10ExBin", "UASFluctECMA05ExBin", "UASFluctFZ10ExMaxLR", "UASFluctFZ05ExMaxLR", "UASFluctOV10ExMaxLR", "UASFluctOV05ExMaxLR")
dVar <- "dHighAnnoyPc"

seeds <- c(25107, 546098, 195, 5937, 102658)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutFluct <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
                          ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres,
                          nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 6.468882
resultsOutFluct$OOB_MAE
[1] 4.835973
resultsOutFluct$Rsquared
[1] 0.636221

Train multiple seeds model


resultsOutFluct <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 6.48319
resultsOutFluct$OOB_MAE
[1] 4.846364
resultsOutFluct$Rsquared
[1] 0.6345453

# store results
resdHiAnnoyFitAB['Abs fluct', 'RMSE'] <- resultsOutFluct$OOB_RMSE
resdHiAnnoyFitAB['Abs fluct', 'MAE'] <- resultsOutFluct$OOB_MAE
resdHiAnnoyFitAB['Abs fluct', 'Rsquared'] <- resultsOutFluct$Rsquared
resdHiAnnoyPermImpAB$AbsFluct <- resultsOutFluct$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutFluct.conimp <- arrange(resultsOutFluct$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutFluct.conimp) + geom_col(aes(x=factor(rownames(resultsOutFluct.conimp), levels=rownames(resultsOutFluct.conimp)), y=CondPermImp), fill=mycolours[4], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("Fluctuation strength") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyFluctConPermimp.svg", width=8, height=2.9, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyFluctConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyFluctConPermimp.pdf", width=8, height=2.9, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyFluctConPermimp.pdf")
}

Selected metric


fluctVar <- "UASFluctECMA10ExBin"
Roughness
Set variables

# Roughness
iVars <- c(absVar, eventVar, ambVar, "UASRoughECMA10ExBin", "UASRoughECMA05ExBin", "UASRoughFZ10ExMaxLR", "UASRoughFZ05ExMaxLR", "UASRoughDW10ExMaxLR", "UASRoughDW05ExMaxLR")
dVar <- "dHighAnnoyPc"

seeds <- c(4701, 52187, 16589, 65217, 16893)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 4001
mtry <- as.integer(length(iVars)/1.5)
Run model

Train preliminary model


nperm <- 5

resultsOutRough <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 6.370114
resultsOutRough$OOB_MAE
[1] 4.823308
resultsOutRough$Rsquared
[1] 0.6515551

Train multiple seeds model


resultsOutRough <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 6.398763
resultsOutRough$OOB_MAE
[1] 4.841106
resultsOutRough$Rsquared
[1] 0.6481992
# store results
resdHiAnnoyFitAB['Abs rough', 'RMSE'] <- resultsOutRough$OOB_RMSE
resdHiAnnoyFitAB['Abs rough', 'MAE'] <- resultsOutRough$OOB_MAE
resdHiAnnoyFitAB['Abs rough', 'Rsquared'] <- resultsOutRough$Rsquared
resdHiAnnoyPermImpAB$AbsRough <- resultsOutRough$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutRough.conimp <- arrange(resultsOutRough$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutRough.conimp) + geom_col(aes(x=factor(rownames(resultsOutRough.conimp), levels=rownames(resultsOutRough.conimp)), y=CondPermImp), fill=mycolours[5], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("Roughness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyRoughConPermimp.svg", width=8, height=2.9, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyRoughConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyRoughConPermimp.pdf", width=8, height=2.9, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyRoughConPermimp.pdf")
}

Selected metric


roughVar <- "UASRoughFZ05ExMaxLR"
Impulsiveness
Set variables
# Impulsiveness
iVars <- c(absVar, eventVar, ambVar, "UASImpulsSHMAvgMaxLR", "UASImpulsSHM05ExMaxLR", "UASImpulsSHMPowAvgMaxLR", "UASImpulsLoudWZAvgMaxLR", "UASImpulsLoudWZ05ExMaxLR", "UASImpulsLoudWZPowAvgMaxLR", "UASImpulsLoudWECMAAvgBin", "UASImpulsLoudWECMA05ExBin", "UASImpulsLoudWECMAPowAvgBin")
dVar <- "dHighAnnoyPc"

seeds <- c(8495, 59867, 5416, 9843, 86)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 501
mtry <- as.integer(length(iVars)/1.5)
Run model

Train preliminary model


nperm <- 5

resultsOutImpuls <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 6.270202
resultsOutImpuls$OOB_MAE
[1] 4.842259
resultsOutImpuls$Rsquared
[1] 0.6582782

Train multiple seeds model


resultsOutImpuls <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 6.270233
resultsOutImpuls$OOB_MAE
[1] 4.84118
resultsOutImpuls$Rsquared
[1] 0.6582749

# store results
resdHiAnnoyFitAB['Abs impuls', 'RMSE'] <- resultsOutImpuls$OOB_RMSE
resdHiAnnoyFitAB['Abs impuls', 'MAE'] <- resultsOutImpuls$OOB_MAE
resdHiAnnoyFitAB['Abs impuls', 'Rsquared'] <- resultsOutImpuls$Rsquared
resdHiAnnoyPermImpAB$AbsImpuls <- resultsOutImpuls$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutImpuls.conimp <- arrange(resultsOutImpuls$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutImpuls.conimp) + geom_col(aes(x=factor(rownames(resultsOutImpuls.conimp), levels=rownames(resultsOutImpuls.conimp)), y=CondPermImp), fill=mycolours[6], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("Impulsiveness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyImpulsConPermimp.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyImpulsConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyImpulsConPermimp.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyImpulsConPermimp.pdf")
}

Selected metric


impulsVar <- "UASImpulsLoudWZAvgMaxLR"

SQM and loudness comparison

Now the highest importance SQMs are ranked against each other, controlling for UAS loudness and ambient LAeq.

Include tonal loudness
Set variables

iVars <- c(absVar, eventVar, ambVar, sharpVar, tonLdVar, fluctVar, roughVar, impulsVar)
dVar <- "dHighAnnoyPc"

seeds <- c(70498, 4, 14986, 453, 864)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1501
mtry <- 3
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 6.112539
resultsOutSQMs1$OOB_MAE
[1] 4.697572
resultsOutSQMs1$Rsquared
[1] 0.6758278

Train multiple seeds model


resultsOutSQMs1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 6.115048
resultsOutSQMs1$OOB_MAE
[1] 4.695767
resultsOutSQMs1$Rsquared
[1] 0.6754898

# store results
resdHiAnnoyFitAB['Abs SQMs inc tonal loud', 'RMSE'] <- resultsOutSQMs1$OOB_RMSE
resdHiAnnoyFitAB['Abs SQMs inc tonal loud', 'MAE'] <- resultsOutSQMs1$OOB_MAE
resdHiAnnoyFitAB['Abs SQMs inc tonal loud', 'Rsquared'] <- resultsOutSQMs1$Rsquared
resdHiAnnoyPermImpAB$AbsSQMs1 <- resultsOutSQMs1$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs1.conimp <- arrange(resultsOutSQMs1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs1.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs1.conimp), levels=rownames(resultsOutSQMs1.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 30))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAbsSQMsTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAbsSQMsTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAbsSQMsTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAbsSQMsTonLdConPermimp.pdf")
}
Exclude tonal loudness
Set variables

iVars <- c(absVar, eventVar, ambVar, sharpVar, tonalVar, fluctVar, roughVar, impulsVar)
dVar <- "dHighAnnoyPc"

seeds <- c(546, 57203, 270835, 60592, 8094)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 4001
mtry <- 3
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 6.081512
resultsOutSQMs2$OOB_MAE
[1] 4.686507
resultsOutSQMs2$Rsquared
[1] 0.6810178

Train multiple seeds model


resultsOutSQMs2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 6.101152
resultsOutSQMs2$OOB_MAE
[1] 4.702343
resultsOutSQMs2$Rsquared
[1] 0.6787822

# store results
resdHiAnnoyFitAB['Abs SQMs no tonal loud', 'RMSE'] <- resultsOutSQMs2$OOB_RMSE
resdHiAnnoyFitAB['Abs SQMs no tonal loud', 'MAE'] <- resultsOutSQMs2$OOB_MAE
resdHiAnnoyFitAB['Abs SQMs no tonal loud', 'Rsquared'] <- resultsOutSQMs2$Rsquared
resdHiAnnoyPermImpAB$AbsSQMs2 <- resultsOutSQMs2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs2.conimp <- arrange(resultsOutSQMs2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs2.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs2.conimp), levels=rownames(resultsOutSQMs2.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 30))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAbsSQMsNoTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAbsSQMsNoTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAbsSQMsNoTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAbsSQMsNoTonLdConPermimp.pdf")
}

Psychoacoustic annoyance metrics

Set variables

iVars <- c(ambVar, "UASPsychAnnoyWidmann", "UASPsychAnnoyMore", "UASPsychAnnoyDi", "UASPsychAnnoyTorija", "UASPsychAnnoyWillemsen", "UASPsychAnnoyBoucher")
dVar <- "dHighAnnoyPc"

seeds <- c(48651, 45, 785123, 65, 5163)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 4001
mtry <- 4
Run model

Train preliminary model


nperm <- 5

resultsOutPA <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 6.88072
resultsOutPA$OOB_MAE
[1] 5.141535
resultsOutPA$Rsquared
[1] 0.5875095

Train multiple seeds model


resultsOutPA <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 6.879401
resultsOutPA$OOB_MAE
[1] 5.138723
resultsOutPA$Rsquared
[1] 0.5876636

# store results
resdHiAnnoyFitAB['Psychoacoustic annoyance', 'RMSE'] <- resultsOutPA$OOB_RMSE
resdHiAnnoyFitAB['Psychoacoustic annoyance', 'MAE'] <- resultsOutPA$OOB_MAE
resdHiAnnoyFitAB['Psychoacoustic annoyance', 'Rsquared'] <- resultsOutPA$Rsquared
resdHiAnnoyPermImpAB$AbsPA <- resultsOutPA$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutPA.conimp <- arrange(resultsOutPA$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutPA.conimp) + geom_col(aes(x=factor(rownames(resultsOutPA.conimp), levels=rownames(resultsOutPA.conimp)), y=CondPermImp), fill=mycolours[10], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 60))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAbsPAConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAbsPAConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAbsPAConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAbsPAConPermimp.pdf")
}

All variables (absolute and difference)

Set variables


iVars <- names(stimDataNum)[which(names(stimDataNum) == 'UASEvents'):which(names(stimDataNum) == 'UASPsychAnnoyBoucher')]
iVars <- iVars[! iVars %in% 'SNRlevel']
iVars <- c(iVars,
           names(stimDataNum)[which(colnames(stimDataNum)=='LAeqLAF90diff'):
                               which(colnames(stimDataNum)=='dPsychAnnoyBoucher')], 'SNRlevel')
dVar <- "dHighAnnoyPc"

seeds <- c(2, 312, 1897, 465978, 821659)

Hyperparameter tuning


p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
              ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1501
mtry <- as.integer(length(iVars)/3.5)

Run model

Train preliminary model


nperm <- 5

resultsOutAbsDiffs <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbsDiffs$OOB_RMSE
[1] 6.145585
resultsOutAbsDiffs$OOB_MAE
[1] 4.687502
resultsOutAbsDiffs$Rsquared
[1] 0.6754223

Train multiple seeds model


resultsOutAbsDiffs <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutAbsDiffs$OOB_RMSE
[1] 6.145665
resultsOutAbsDiffs$OOB_MAE
[1] 4.694338
resultsOutAbsDiffs$Rsquared
[1] 0.676116
# store results
resdHiAnnoyFitAB['All vars', 'RMSE'] <- resultsOutAbsDiffs$OOB_RMSE
resdHiAnnoyFitAB['All vars', 'MAE'] <- resultsOutAbsDiffs$OOB_MAE
resdHiAnnoyFitAB['All vars', 'Rsquared'] <- resultsOutAbsDiffs$Rsquared
resdHiAnnoyPermImpAB$AllVars <- resultsOutAbsDiffs$conditional_permimp

Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutAbsDiffs.conimp <- arrange(resultsOutAbsDiffs$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutAbsDiffs.conimp) + geom_col(aes(x=factor(rownames(resultsOutAbsDiffs.conimp), levels=rownames(resultsOutAbsDiffs.conimp)), y=CondPermImp), fill=mycolours[9], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllVarsConPermimp.svg", width=8, height=26, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllVarsConPermimp.svg")

  ggsave(filename="PtsABdHiAnnoyAllVarsConPermimp.pdf", width=8, height=26, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllVarsConPermimp.pdf")
}

# Plot only positive values

resultsOutAbsDiffs.conimpPtv <- resultsOutAbsDiffs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > 0)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbsDiffs.conimpPtv) + geom_col(aes(x=factor(rownames(resultsOutAbsDiffs.conimpPtv), levels=rownames(resultsOutAbsDiffs.conimpPtv)), y=CondPermImp), fill=mycolours[9], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllVarsConPermimpPtv.svg", width=8, height=22, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllVarsConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllVarsConPermimpPtv.pdf", width=8, height=22, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllVarsConPermimp.pdf")
}

# Plot only values within 1% of the maximum

resultsOutAbsDiffs.conimp1pc <- resultsOutAbsDiffs.conimp |>
                                          rownames_to_column('Metric') |>
                                                filter_if(is.numeric, all_vars(. > max(resultsOutAbsDiffs.conimp)/100)) |>
                                                      column_to_rownames('Metric')

pBar <- ggplot(resultsOutAbsDiffs.conimp1pc) + geom_col(aes(x=factor(rownames(resultsOutAbsDiffs.conimp1pc), levels=rownames(resultsOutAbsDiffs.conimp1pc)), y=CondPermImp), fill=mycolours[9], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllVarsConPermimp1pc.svg", width=8, height=7, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllVarsConPermimp1pc.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllVarsConPermimp1pc.pdf", width=8, height=7, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllVarsConPermimp1pc.pdf")
}

Selected metric


allVar <- "UASLoudECMAPowAvgBin"

dSQM analysis

Individual SQMs

dSharpness
Set variables

iVars <- c(allVar, eventVar, ambVar, "dSharpAurISO3PowAvgBin", "dSharpAurISO305ExBin", "dSharpAurSHMPowAvgBin", "dSharpAurSHM05ExBin", "dTonShpAurSHMPowAvgBin", "dTonShpAurSHM05ExBin", "PartTonShpAurSHMPowAvgBin",
           "PartTonShpAurSHM05ExBin", "UASSharpAurISO3PowAvgBin", "UASSharpAurISO305ExBin", "UASSharpAurSHMPowAvgBin", "UASSharpAurSHM05ExBin", "UASSharpAurISO1PowAvgBin", "UASSharpAurISO105ExBin", "UASSharpvBISO1PowAvgBin", "UASSharpvBISO105ExBin", "UASSharpDINPowAvgBin", "UASSharpDIN05ExBin", "UASSharpAurISO1MedBin",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dHighAnnoyPc"

seeds <- c(84194, 905, 64815, 928054, 625091, 582031)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/2.25)
Run model

Train preliminary model


nperm <- 10

resultsOutSharp <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 6.03978
resultsOutSharp$OOB_MAE
[1] 4.513288
resultsOutSharp$Rsquared
[1] 0.6875739

Train multiple seeds model


resultsOutSharp <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSharp$OOB_RMSE
[1] 6.105942
resultsOutSharp$OOB_MAE
[1] 4.555393
resultsOutSharp$Rsquared
[1] 0.6794885
# store results
resdHiAnnoyFitAB['All sharp', 'RMSE'] <- resultsOutSharp$OOB_RMSE
resdHiAnnoyFitAB['All sharp', 'MAE'] <- resultsOutSharp$OOB_MAE
resdHiAnnoyFitAB['All sharp', 'Rsquared'] <- resultsOutSharp$Rsquared
resdHiAnnoyPermImpAB$AllSharp <- resultsOutSharp$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSharp.conimp <- arrange(resultsOutSharp$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSharp.conimp) + geom_col(aes(x=factor(rownames(resultsOutSharp.conimp), levels=rownames(resultsOutSharp.conimp)), y=CondPermImp), fill=mycolours[2], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("All sharpness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllSharpConPermimp.svg", width=8, height=5, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllSharpConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllSharpConPermimp.pdf", width=8, height=5, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllSharpConPermimp.pdf")
}

Selected metric


allSharpVar <- "dSharpAurSHMPowAvgBin"
dTonal loudness and dtonality
Set variables

iVars <- c(allVar, eventVar, ambVar, "dTonalECMAAvgMaxLR", "dTonalSHMInt05ExMaxLR", "dTonalSHMIntAvgMaxLR", "dTonalECMA05ExMaxLR", "dTonalAwSHMAvgMaxLR",   "dTonalAwSHM05ExMaxLR", "dTonalAwSHMIntAvgMaxLR",   "dTonalAwSHMInt05ExMaxLR", "dTonLdECMAPowAvgBin", "dTonLdECMA05ExBin", "dTonShpAurSHMPowAvgBin",
           "dTonShpAurSHM05ExBin", "PartTonLdSHMPowAvgBin", "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR",  "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR",     "UASTonalAwSHMInt05ExMaxLR", "UASTonLdECMAPowAvgBin", "UASTonLdECMA05ExBin", "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR",
         "UASTonShpAurSHMPowAvgBin", "UASTonShpAurSHM05ExBin")
dVar <- "dHighAnnoyPc"
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p


seeds <- c(561684, 104798, 1536, 48, 48561)

Selected hyperparameters


ntree <- 501
mtry <- as.integer(length(iVars)/2.25)
Run model

Train preliminary model

# Tonality with tonal loudness

nperm <- 5

resultsOutTonal1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 6.240305
resultsOutTonal1$OOB_MAE
[1] 4.837859
resultsOutTonal1$Rsquared
[1] 0.6655154

Train multiple seeds model

# Tonality with tonal loudness

resultsOutTonal1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal1$OOB_RMSE
[1] 6.300441
resultsOutTonal1$OOB_MAE
[1] 4.883414
resultsOutTonal1$Rsquared
[1] 0.6581737
# store results
resdHiAnnoyFitAB['All tonal inc loud', 'RMSE'] <- resultsOutTonal1$OOB_RMSE
resdHiAnnoyFitAB['All tonal inc loud', 'MAE'] <- resultsOutTonal1$OOB_MAE
resdHiAnnoyFitAB['All tonal inc loud', 'Rsquared'] <- resultsOutTonal1$Rsquared
resdHiAnnoyPermImpAB$AllTonal1 <- resultsOutTonal1$conditional_permimp
Plot results

par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal1.conimp <- arrange(resultsOutTonal1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal1.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal1.conimp), levels=rownames(resultsOutTonal1.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("All tonality inc. tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 50))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllTonalLdConPermimp.svg", width=8, height=6, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllTonalLdConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllTonalLdConPermimp.pdf", width=8, height=6, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllTonalLdConPermimp.pdf")
}

Selected metric


allTonLdVar <- "UASTonLdECMAPowAvgBin"
dTonality without dtonal loudness
Set variables

iVars <- c(allVar, eventVar, ambVar, "dTonalECMAAvgMaxLR", "dTonalSHMInt05ExMaxLR", "dTonalSHMIntAvgMaxLR", "dTonalECMA05ExMaxLR", "dTonalAwSHMAvgMaxLR",   "dTonalAwSHM05ExMaxLR", "dTonalAwSHMIntAvgMaxLR",   "dTonalAwSHMInt05ExMaxLR", "UASTonalECMAAvgMaxLR", "UASTonalSHMInt05ExMaxLR", "UASTonalSHMIntAvgMaxLR", "UASTonalECMA05ExMaxLR", "UASTonalAwSHMAvgMaxLR",   "UASTonalAwSHM05ExMaxLR",   "UASTonalAwSHMIntAvgMaxLR", "UASTonalAwSHMInt05ExMaxLR",    "UASTonalAurAvgMaxLR", "UASTonalAur05ExMaxLR", "UASTonalAur10ExMaxLR")
dVar <- "dHighAnnoyPc"

seeds <- c(410865, 2954, 70812, 203, 7984)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 501
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model

# Tonality

nperm <- 5

resultsOutTonal2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 6.399904
resultsOutTonal2$OOB_MAE
[1] 4.887571
resultsOutTonal2$Rsquared
[1] 0.6451589

Train multiple seeds model

# Tonality

resultsOutTonal2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutTonal2$OOB_RMSE
[1] 6.393998
resultsOutTonal2$OOB_MAE
[1] 4.900818
resultsOutTonal2$Rsquared
[1] 0.6463381

# store results
resdHiAnnoyFitAB['All tonal no loud', 'RMSE'] <- resultsOutTonal2$OOB_RMSE
resdHiAnnoyFitAB['All tonal no loud', 'MAE'] <- resultsOutTonal2$OOB_MAE
resdHiAnnoyFitAB['All tonal no loud', 'Rsquared'] <- resultsOutTonal2$Rsquared
resdHiAnnoyPermImpAB$AllTonal2 <- resultsOutTonal2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutTonal2.conimp <- arrange(resultsOutTonal2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutTonal2.conimp) + geom_col(aes(x=factor(rownames(resultsOutTonal2.conimp), levels=rownames(resultsOutTonal2.conimp)), y=CondPermImp), fill=mycolours[3], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("All tonality w/o tonal loudness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 100))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllTonalConPermimp.svg", width=8, height=4.8, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllTonalConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllTonalConPermimp.pdf", width=8, height=4.8, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllTonalConPermimp.pdf")
}

Selected metric


allTonalVar <- "UASTonalAwSHMInt05ExMaxLR"
dFluctuation strength
Set variables

# Fluctuation strength
iVars <- c(allVar, eventVar, ambVar, "dFluctECMA10ExBin", "dFluctECMA05ExBin", "dFluctOV10ExMaxLR", "dFluctOV05ExMaxLR", "UASFluctOldSHM10ExBin", "UASFluctOldSHM05ExBin", "UASFluctECMA10ExBin", "UASFluctECMA05ExBin", "UASFluctFZ10ExMaxLR", "UASFluctFZ05ExMaxLR", "UASFluctOV10ExMaxLR", "UASFluctOV05ExMaxLR")
dVar <- "dHighAnnoyPc"

seeds <- c(418657, 84, 1630, 18659, 3687)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 251
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutFluct <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 6.439173
resultsOutFluct$OOB_MAE
[1] 4.789859
resultsOutFluct$Rsquared
[1] 0.6391434

Train multiple seeds model


resultsOutFluct <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutFluct$OOB_RMSE
[1] 6.4464
resultsOutFluct$OOB_MAE
[1] 4.815919
resultsOutFluct$Rsquared
[1] 0.6384767

# store results
resdHiAnnoyFitAB['All fluct', 'RMSE'] <- resultsOutFluct$OOB_RMSE
resdHiAnnoyFitAB['All fluct', 'MAE'] <- resultsOutFluct$OOB_MAE
resdHiAnnoyFitAB['All fluct', 'Rsquared'] <- resultsOutFluct$Rsquared
resdHiAnnoyPermImpAB$AllFluct <- resultsOutFluct$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutFluct.conimp <- arrange(resultsOutFluct$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutFluct.conimp) + geom_col(aes(x=factor(rownames(resultsOutFluct.conimp), levels=rownames(resultsOutFluct.conimp)), y=CondPermImp), fill=mycolours[4], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("All fluctuation strength") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllFluctConPermimp.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllFluctConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllFluctConPermimp.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllFluctConPermimp.pdf")
}

Selected metric


allFluctVar <- "UASFluctECMA10ExBin"
dRoughness
Set variables

# Roughness
iVars <- c(allVar, eventVar, ambVar, "dRoughECMA10ExBin", "dRoughECMA05ExBin", "dRoughFZ10ExMaxLR", "dRoughFZ05ExMaxLR", "UASRoughECMA10ExBin", "UASRoughECMA05ExBin", "UASRoughFZ10ExMaxLR", "UASRoughFZ05ExMaxLR", "UASRoughDW10ExMaxLR", "UASRoughDW05ExMaxLR")
dVar <- "dHighAnnoyPc"

seeds <- c(69851, 85109, 410986, 1563, 896)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1501
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutRough <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 6.494263
resultsOutRough$OOB_MAE
[1] 4.835709
resultsOutRough$Rsquared
[1] 0.633995

Train multiple seeds model


resultsOutRough <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutRough$OOB_RMSE
[1] 6.495009
resultsOutRough$OOB_MAE
[1] 4.843281
resultsOutRough$Rsquared
[1] 0.6340358
# store results
resdHiAnnoyFitAB['All rough', 'RMSE'] <- resultsOutRough$OOB_RMSE
resdHiAnnoyFitAB['All rough', 'MAE'] <- resultsOutRough$OOB_MAE
resdHiAnnoyFitAB['All rough', 'Rsquared'] <- resultsOutRough$Rsquared
resdHiAnnoyPermImpAB$AllRough <- resultsOutRough$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutRough.conimp <- arrange(resultsOutRough$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutRough.conimp) + geom_col(aes(x=factor(rownames(resultsOutRough.conimp), levels=rownames(resultsOutRough.conimp)), y=CondPermImp), fill=mycolours[5], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("All roughness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllRoughConPermimp.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllRoughConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllRoughConPermimp.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllRoughConPermimp.pdf")
}

Selected metric


allRoughVar <- "dRoughFZ05ExMaxLR"
dImpulsiveness
Set variables
# Impulsiveness
iVars <- c(allVar, eventVar, ambVar, "dImpulsSHMAvgMaxLR", "dImpulsSHM05ExMaxLR", "dImpulsSHMPowAvgMaxLR",
           "dImpulsLoudWZAvgMaxLR", "dImpulsLoudWZ05ExMaxLR", "dImpulsLoudWZPowAvgMaxLR",
           "dImpulsLoudWECMAAvgBin", "dImpulsLoudWECMA05ExBin", "dImpulsLoudWECMAPowAvgBin", "UASImpulsSHMAvgMaxLR", "UASImpulsSHM05ExMaxLR", "UASImpulsSHMPowAvgMaxLR", "UASImpulsLoudWZAvgMaxLR", "UASImpulsLoudWZ05ExMaxLR", "UASImpulsLoudWZPowAvgMaxLR", "UASImpulsLoudWECMAAvgBin", "UASImpulsLoudWECMA05ExBin", "UASImpulsLoudWECMAPowAvgBin")
dVar <- "dHighAnnoyPc"

seeds <- c(418659, 7805, 38475, 65834, 1653)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 5501
mtry <- as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutImpuls <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 6.462745
resultsOutImpuls$OOB_MAE
[1] 4.980449
resultsOutImpuls$Rsquared
[1] 0.6423632

Train multiple seeds model


resultsOutImpuls <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutImpuls$OOB_RMSE
[1] 6.478918
resultsOutImpuls$OOB_MAE
[1] 4.979735
resultsOutImpuls$Rsquared
[1] 0.6409678

# store results
resdHiAnnoyFitAB['All impuls', 'RMSE'] <- resultsOutImpuls$OOB_RMSE
resdHiAnnoyFitAB['All impuls', 'MAE'] <- resultsOutImpuls$OOB_MAE
resdHiAnnoyFitAB['All impuls', 'Rsquared'] <- resultsOutImpuls$Rsquared
resdHiAnnoyPermImpAB$AllImpuls <- resultsOutImpuls$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutImpuls.conimp <- arrange(resultsOutImpuls$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutImpuls.conimp) + geom_col(aes(x=factor(rownames(resultsOutImpuls.conimp), levels=rownames(resultsOutImpuls.conimp)), y=CondPermImp), fill=mycolours[6], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + ggtitle("All impulsiveness") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip()
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllImpulsConPermimp.svg", width=8, height=5.6, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllImpulsConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllImpulsConPermimp.pdf", width=8, height=5.6, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllImpulsConPermimp.pdf")
}

Selected metric


allImpulsVar <- "UASImpulsLoudWZAvgMaxLR"

dSQM and loudness comparison

Now the highest importance dSQMs are ranked against each other, controlling for loudness difference.

Include dtonal loudness
Set variables

iVars <- c(allVar, eventVar, ambVar, allSharpVar, allTonLdVar, allFluctVar, allRoughVar, allImpulsVar)
dVar <- "dHighAnnoyPc"

seeds <- c(98465, 54163, 6541, 36485, 849675)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 1001
mtry <- 3
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs1 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 6.263055
resultsOutSQMs1$OOB_MAE
[1] 4.827668
resultsOutSQMs1$Rsquared
[1] 0.6595923

Train multiple seeds model


resultsOutSQMs1 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs1$OOB_RMSE
[1] 6.239049
resultsOutSQMs1$OOB_MAE
[1] 4.812234
resultsOutSQMs1$Rsquared
[1] 0.6620318

# store results
resdHiAnnoyFitAB['All SQMs inc tonal loud', 'RMSE'] <- resultsOutSQMs1$OOB_RMSE
resdHiAnnoyFitAB['All SQMs inc tonal loud', 'MAE'] <- resultsOutSQMs1$OOB_MAE
resdHiAnnoyFitAB['All SQMs inc tonal loud', 'Rsquared'] <- resultsOutSQMs1$Rsquared
resdHiAnnoyPermImpAB$AllSQMs1 <- resultsOutSQMs1$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs1.conimp <- arrange(resultsOutSQMs1$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs1.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs1.conimp), levels=rownames(resultsOutSQMs1.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 40))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllSQMsTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllSQMsTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllSQMsTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllSQMsTonLdConPermimp.pdf")
}
Exclude tonal loudness
Set variables

iVars <- c(allVar, eventVar, ambVar, allSharpVar, allTonalVar, allFluctVar, allRoughVar, allImpulsVar)
dVar <- "dHighAnnoyPc"

seeds <- c(49865, 7852, 845961, 410583, 36748)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
             ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 2501
mtry <- as.integer(length(iVars)/1.6)
Run model

Train preliminary model


nperm <- 5

resultsOutSQMs2 <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 6.438043
resultsOutSQMs2$OOB_MAE
[1] 4.933283
resultsOutSQMs2$Rsquared
[1] 0.6391176

Train multiple seeds model


resultsOutSQMs2 <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutSQMs2$OOB_RMSE
[1] 6.422652
resultsOutSQMs2$OOB_MAE
[1] 4.923783
resultsOutSQMs2$Rsquared
[1] 0.6408381

# store results
resdHiAnnoyFitAB['All SQMs no tonal loud', 'RMSE'] <- resultsOutSQMs2$OOB_RMSE
resdHiAnnoyFitAB['All SQMs no tonal loud', 'MAE'] <- resultsOutSQMs2$OOB_MAE
resdHiAnnoyFitAB['All SQMs no tonal loud', 'Rsquared'] <- resultsOutSQMs2$Rsquared
resdHiAnnoyPermImpAB$AllSQMs2 <- resultsOutSQMs2$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutSQMs2.conimp <- arrange(resultsOutSQMs2$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutSQMs2.conimp) + geom_col(aes(x=factor(rownames(resultsOutSQMs2.conimp), levels=rownames(resultsOutSQMs2.conimp)), y=CondPermImp), fill=mycolours[7], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 40))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllSQMsNoTonLdConPermimp.svg", width=8, height=2.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllSQMsNoTonLdConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllSQMsNoTonLdConPermimp.pdf", width=8, height=2.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllSQMsNoTonLdConPermimp.pdf")
}

dPsychoacoustic annoyance metrics

Set variables

iVars <- c(ambVar, "dPsychAnnoyWidmann", "dPsychAnnoyMore", "dPsychAnnoyDi", "dPsychAnnoyTorija", "dPsychAnnoyWillemsen", "dPsychAnnoyBoucher", "UASPsychAnnoyWidmann", "UASPsychAnnoyMore", "UASPsychAnnoyDi", "UASPsychAnnoyTorija", "UASPsychAnnoyWillemsen", "UASPsychAnnoyBoucher")
dVar <- "dHighAnnoyPc"

seeds <- c(835702, 54, 470912, 652, 55297)
Hyperparameter tuning

p <- mtryTune(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2],
              ntrees=ntrees, minsplit=minsplit, minbucket=minbucket)
p

Selected hyperparameters


ntree <- 4001
mtry <-  as.integer(length(iVars)/1.25)
Run model

Train preliminary model


nperm <- 5

resultsOutPA <- crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds[1:2], ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 6.809738
resultsOutPA$OOB_MAE
[1] 5.143005
resultsOutPA$Rsquared
[1] 0.5960921

Train multiple seeds model


resultsOutPA <- multi_crfReg(dataIn=stimDataNum, iVars=iVars, dVar=dVar, seeds=seeds, ntree=ntree, mtry=mtry, permImpCondThres=permImpCondThres, nperm=nperm, minsplit=minsplit, minbucket=minbucket)

# print model prediction results
resultsOutPA$OOB_RMSE
[1] 6.807571
resultsOutPA$OOB_MAE
[1] 5.140512
resultsOutPA$Rsquared
[1] 0.5963174

# store results
resdHiAnnoyFitAB['All Psychoacoustic annoyance', 'RMSE'] <- resultsOutPA$OOB_RMSE
resdHiAnnoyFitAB['All Psychoacoustic annoyance', 'MAE'] <- resultsOutPA$OOB_MAE
resdHiAnnoyFitAB['All Psychoacoustic annoyance', 'Rsquared'] <- resultsOutPA$Rsquared
resdHiAnnoyPermImpAB$AllPA <- resultsOutPA$conditional_permimp
Plot results
par(mai=c(0,3,0,0))

# plot conditional importance
resultsOutPA.conimp <- arrange(resultsOutPA$conditional_permimp, desc(row_number()))

pBar <- ggplot(resultsOutPA.conimp) + geom_col(aes(x=factor(rownames(resultsOutPA.conimp), levels=rownames(resultsOutPA.conimp)), y=CondPermImp), fill=mycolours[10], width=0.5) + labs(x="Variable", y="Conditional variable permutation importance (% highly annoyed)") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 70))
pBar


if (saveplots){
  ggsave(filename="PtsABdHiAnnoyAllPAConPermimp.svg", width=8, height=4.4, path=file.path(outFigPath, "svg"))
  unlink("PtsABdHiAnnoyAllPAConPermimp.svg")
  
  ggsave(filename="PtsABdHiAnnoyAllPAConPermimp.pdf", width=8, height=4.4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABdHiAnnoyAllPAConPermimp.pdf")
}

Save the results outputs to file


if (savedata){
  utils::write.csv(resdHiAnnoyFitAB, paste(outDataPath, "\\PtsABCRFdHiAnnoyOOBFit.csv", sep=""))
  ii <- 0
  temp = list()
  for (res in resdHiAnnoyPermImpAB){
    ii <- ii + 1
    temp[[ii]] <- as.data.frame(resdHiAnnoyPermImpAB[ii])
    names(temp[[ii]]) <- names(resdHiAnnoyPermImpAB[ii])
  }
  openxlsx::write.xlsx(temp, paste(outDataPath, "\\PtsABCRFdHiAnnoyConPermimp.xlsx",
                                   sep=""),
                       rowNames=TRUE)
}

Parts A&B summary

Summary of results for Parts A & B combined

With tonal loudness

Absolute variables

# combine the annoyance perm importance results

# convert each result to a tibble with rownames added to a column, renaming the data column to 'dAnnoy' etc.
resdAnnoyMnAbsPermImpTblAB <- as.data.frame(resdAnnoyMnPermImpAB$AbsSQMs1/max(resdAnnoyMnPermImpAB$AbsSQMs1)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdAnnoyMnAbsPermImpTblAB)[2] <- "dAnnoy"

resdHiAnnoyAbsPermImpTblAB <- as.data.frame(resdHiAnnoyPermImpAB$AbsSQMs1/max(resdHiAnnoyPermImpAB$AbsSQMs1)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdHiAnnoyAbsPermImpTblAB)[2] <- "dHiAnnoy"

# merge the dataframes
resAbsPermImpTblAB <- list(resdAnnoyMnAbsPermImpTblAB, resdHiAnnoyAbsPermImpTblAB) |>
  purrr::reduce(merge, by = c('Variable'), all = T)

# rename the columns
colnames(resAbsPermImpTblAB)[2:3] <- c("Mean change in annoyance", "%HA | HA' (amb.)")
resAbsPermImpTblAB[is.na(resAbsPermImpTblAB)] <- 0

resAbsAB <- tidyr::pivot_longer(resAbsPermImpTblAB, cols=-Variable, names_to="Outcome", values_to="Imp")

# reorder res tibble, descending by the variable Imp grouped sum and create column with new group order as a factor
resAbsAB <- resAbsAB |> mutate(Variable_sum = sum(Imp), .by=Variable) |> arrange(desc(Variable_sum)) |> group_by(Variable_sum, Variable) |>
   mutate(Order = cur_group_id()) |> mutate(Order = as.factor(Order)) |> arrange(desc(Order))

# Reorder outcome levels
resAbsAB$Outcome <- factor(resAbsAB$Outcome, levels=c("Mean change in annoyance", "%HA | HA' (amb.)"))

# plot res as horizontal bar chart, with Imp as y axis, Variable as x axis, Outcome as fill, and Variable_sum as order, relabel x axis with Variable names
pBar <- ggplot(resAbsAB) + geom_col(aes(fill=Outcome, y=Imp, x=Order), colour='grey35', linewidth=0,  width=0.75, show.legend=TRUE) + labs(x="Variable", y="Normalised conditional variable\npermutation importance") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2)) + coord_flip(ylim=c(0, 1)) + scale_fill_manual(values=mycolours, labels=c(expression(paste(bar(Delta~A))), "%HA | HA' (amb.)")) + scale_x_discrete(labels=unique(rev(resAbsAB$Variable))) + guides(fill=guide_legend(title='Outcome'))
pBar + scale_y_continuous(breaks=seq(0, 1, by=0.5))


if (saveplots){
  ggsave(filename="PtsABcrfAbsSQMsSummary.svg", width=8, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABcrfAbsSQMsSummary.svg")

  ggsave(filename="PtsABcrfAbsSQMsSummary.pdf", width=8, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABcrfAbsSQMsSummary.pdf")
}


# plot res as horizontal bar chart, with Imp as y axis, Variable as x axis, Outcome as fill, and Variable_sum as order, relabel x axis with Variable names
pBar <- ggplot(resAbsAB) + geom_col(aes(fill=Outcome, y=Imp, x=Order), colour='grey35', linewidth=0,  width=0.75, show.legend=TRUE) + labs(x="Variable", y="Normalised conditional variable\npermutation importance") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2), legend.position = "top") + coord_flip(ylim=c(0, 1)) + scale_fill_manual(values=mycolours, labels=c(expression(paste(bar(Delta~A))), "%HA | HA' (amb.)")) + scale_x_discrete(labels=unique(rev(resAbsAB$Variable))) + guides(fill=guide_legend(title='Outcome', nrow=2, ncol=1))
pBar + scale_y_continuous(breaks=seq(0, 1, by=0.5))


if (saveplots){
  ggsave(filename="PtsABcrfAbsSQMsSummaryNw.svg", width=4, height=4, path=file.path(outFigPath, "svg"))
  unlink("PtsABcrfAbsSQMsSummary.svg")

  ggsave(filename="PtsABcrfAbsSQMsSummaryNw.pdf", width=4, height=4, path=file.path(outFigPath, "pdf"))
  unlink("PtsABcrfAbsSQMsSummary.pdf")
}

All variables

# combine the annoyance perm importance results

# convert each result to a tibble with rownames added to a column, renaming the data column to 'dAnnoy' etc.
resdAnnoyMnAllPermImpTblAB <- as.data.frame(resdAnnoyMnPermImpAB$AllSQMs1/max(resdAnnoyMnPermImpAB$AllSQMs1)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdAnnoyMnAllPermImpTblAB)[2] <- "dAnnoy"

resdHiAnnoyAllPermImpTblAB <- as.data.frame(resdHiAnnoyPermImpAB$AllSQMs1/max(resdHiAnnoyPermImpAB$AllSQMs1)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdHiAnnoyAllPermImpTblAB)[2] <- "dHiAnnoy"

# merge the dataframes
resAllPermImpTblAB <- list(resdAnnoyMnAllPermImpTblAB, resdHiAnnoyAllPermImpTblAB) |>
  purrr::reduce(merge, by = c('Variable'), all = T)

# rename the columns
colnames(resAllPermImpTblAB)[2:3] <- c("Mean change in annoyance", "%HA | HA' (amb.)")
resAllPermImpTblAB[is.na(resAllPermImpTblAB)] <- 0

resAllAB <- tidyr::pivot_longer(resAllPermImpTblAB, cols=-Variable, names_to="Outcome", values_to="Imp")

# reorder res tibble, descending by the variable Imp grouped sum and create column with new group order as a factor
resAllAB <- resAllAB |> mutate(Variable_sum = sum(Imp), .by=Variable) |> arrange(desc(Variable_sum)) |> group_by(Variable_sum, Variable) |>
   mutate(Order = cur_group_id()) |> mutate(Order = as.factor(Order)) |> arrange(desc(Order))

# Reorder outcome levels
resAllAB$Outcome <- factor(resAllAB$Outcome, levels=c("Mean change in annoyance", "%HA | HA' (amb.)"))

# plot res as horizontal bar chart, with Imp as y axis, Variable as x axis, Outcome as fill, and Variable_sum as order, relabel x axis with Variable names
pBar <- ggplot(resAllAB) + geom_col(aes(fill=Outcome, y=Imp, x=Order), colour='grey35', linewidth=0,  width=0.75, show.legend=TRUE) + labs(x="Variable", y="Normalised conditional variable permutation importance") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2), legend.position = "right") + coord_flip(ylim=c(-0.1, 1.1)) + scale_fill_manual(values=mycolours) + scale_x_discrete(labels=unique(rev(resAllAB$Variable)))
pBar + scale_y_continuous(breaks=seq(0, 1, by=0.5))


if (saveplots){
  ggsave(filename="PtsABcrfAllSQMsSummary.svg", width=8, height=3, path=file.path(outFigPath, "svg"))
  unlink("PtsABcrfAllSQMsSummary.svg")
  
  ggsave(filename="PtsABcrfAllSQMsSummary.pdf", width=8, height=3, path=file.path(outFigPath, "pdf"))
  unlink("PtsABcrfAllSQMsSummary.pdf")
}

No tonal loudness

Absolute variables

# combine the annoyance perm importance results

# convert each result to a tibble with rownames added to a column, renaming the data column to 'dAnnoy' etc.
resdAnnoyMnAbsPermImpNoTonLdTblAB <- as.data.frame(resdAnnoyMnPermImpAB$AbsSQMs2/max(resdAnnoyMnPermImpAB$AbsSQMs2)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdAnnoyMnAbsPermImpNoTonLdTblAB)[2] <- "dAnnoy"

resdHiAnnoyAbsPermImpNoTonLdTblAB <- as.data.frame(resdHiAnnoyPermImpAB$AbsSQMs2/max(resdHiAnnoyPermImpAB$AbsSQMs2)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdHiAnnoyAbsPermImpNoTonLdTblAB)[2] <- "dHiAnnoy"

# merge the dataframes
resAbsPermImpNoTonLdTblAB <- list(resdAnnoyMnAbsPermImpNoTonLdTblAB, resdHiAnnoyAbsPermImpNoTonLdTblAB) |>
  purrr::reduce(merge, by = c('Variable'), all = T)

# rename the columns
colnames(resAbsPermImpNoTonLdTblAB)[2:3] <- c("Mean change in annoyance", "%HA | HA' (amb.)")
resAbsPermImpNoTonLdTblAB[is.na(resAbsPermImpNoTonLdTblAB)] <- 0

resAbsNoTonLdAB <- tidyr::pivot_longer(resAbsPermImpNoTonLdTblAB, cols=-Variable, names_to="Outcome", values_to="Imp")

# reorder res tibble, descending by the variable Imp grouped sum and create column with new group order as a factor
resAbsNoTonLdAB <- resAbsNoTonLdAB |> mutate(Variable_sum = sum(Imp), .by=Variable) |> arrange(desc(Variable_sum)) |> group_by(Variable_sum, Variable) |>
   mutate(Order = cur_group_id()) |> mutate(Order = as.factor(Order)) |> arrange(desc(Order))

# Reorder outcome levels
resAbsNoTonLdAB$Outcome <- factor(resAbsNoTonLdAB$Outcome, levels=c("Mean change in annoyance", "%HA | HA' (amb.)"))

# plot res as horizontal bar chart, with Imp as y axis, Variable as x axis, Outcome as fill, and Variable_sum as order, relabel x axis with Variable names
pBar <- ggplot(resAbsNoTonLdAB) + geom_col(aes(fill=Outcome, y=Imp, x=Order), colour='grey35', linewidth=0,  width=0.75, show.legend=TRUE) + labs(x="Variable", y="Normalised conditional variable permutation importance") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2), legend.position = "right") + coord_flip(ylim=c(-0.1, 1.1)) + scale_fill_manual(values=mycolours) + scale_x_discrete(labels=unique(rev(resAbsNoTonLdAB$Variable)))
pBar + scale_y_continuous(breaks=seq(0, 1, by=0.5))


if (saveplots){
  ggsave(filename="PtsABcrfAbsSQMsNoTonLdSummary.svg", width=8, height=3, path=file.path(outFigPath, "svg"))
  unlink("PtsABcrfAbsSQMsNoTonLdSummary.svg")
  
  ggsave(filename="PtsABcrfAbsSQMsNoTonLdSummary.pdf", width=8, height=3, path=file.path(outFigPath, "pdf"))
  unlink("PtsABcrfAbsSQMsNoTonLdSummary.pdf")
}

All variables

# combine the annoyance perm importance results

# convert each result to a tibble with rownames added to a column, renaming the data column to 'dAnnoy' etc.
resdAnnoyMnAllPermImpNoTonLdTblAB <- as.data.frame(resdAnnoyMnPermImpAB$AllSQMs2/max(resdAnnoyMnPermImpAB$AllSQMs2)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdAnnoyMnAllPermImpNoTonLdTblAB)[2] <- "dAnnoy"

resdHiAnnoyAllPermImpNoTonLdTblAB <- as.data.frame(resdHiAnnoyPermImpAB$AllSQMs2/max(resdHiAnnoyPermImpAB$AllSQMs2)) |>
  tibble::rownames_to_column(var='Variable')
colnames(resdHiAnnoyAllPermImpNoTonLdTblAB)[2] <- "dHiAnnoy"

# merge the dataframes
resAllPermImpNoTonLdTblAB <- list(resdAnnoyMnAllPermImpNoTonLdTblAB, resdHiAnnoyAllPermImpNoTonLdTblAB) |>
  purrr::reduce(merge, by = c('Variable'), all = T)

# rename the columns
colnames(resAllPermImpNoTonLdTblAB)[2:3] <- c("Mean change in annoyance", "%HA | HA' (amb.)")
resAllPermImpNoTonLdTblAB[is.na(resAllPermImpNoTonLdTblAB)] <- 0

resAllNoTonLdAB <- tidyr::pivot_longer(resAllPermImpNoTonLdTblAB, cols=-Variable, names_to="Outcome", values_to="Imp")

# reorder res tibble, descending by the variable Imp grouped sum and create column with new group order as a factor
resAllNoTonLdAB <- resAllNoTonLdAB |> mutate(Variable_sum = sum(Imp), .by=Variable) |> arrange(desc(Variable_sum)) |> group_by(Variable_sum, Variable) |>
   mutate(Order = cur_group_id()) |> mutate(Order = as.factor(Order)) |> arrange(desc(Order))

# Reorder outcome levels
resAllNoTonLdAB$Outcome <- factor(resAllNoTonLdAB$Outcome, levels=c("Mean change in annoyance", "%HA | HA' (amb.)"))

# plot res as horizontal bar chart, with Imp as y axis, Variable as x axis, Outcome as fill, and Variable_sum as order, relabel x axis with Variable names
pBar <- ggplot(resAllNoTonLdAB) + geom_col(aes(fill=Outcome, y=Imp, x=Order), colour='grey35', linewidth=0,  width=0.75, show.legend=TRUE) + labs(x="Variable", y="Normalised conditional variable permutation importance") + theme(text = element_text(family = "serif"), panel.grid=element_line(color = rgb(235, 235, 235, 100, maxColorValue = 255), linewidth = 0.25, linetype = 2), legend.position = "right") + coord_flip(ylim=c(-0.1, 1.1)) + scale_fill_manual(values=mycolours) + scale_x_discrete(labels=unique(rev(resAllNoTonLdAB$Variable)))
pBar + scale_y_continuous(breaks=seq(0, 1, by=0.5))


if (saveplots){
  ggsave(filename="PtsABcrfAllSQMsNoTonLdSummary.svg", width=8, height=3, path=file.path(outFigPath, "svg"))
  unlink("PtsABcrfAllSQMsNoTonLdSummary.svg")
  
  ggsave(filename="PtsABcrfAllSQMsNoTonLdSummary.pdf", width=8, height=3, path=file.path(outFigPath, "pdf"))
  unlink("PtsABcrfAllSQMsNoTonLdSummary.pdf")
}

Save the results outputs to file

# Make a list of the summary results
resSummary <- list(resAbsAB, resAllAB, resAbsNoTonLdAB, resAllNoTonLdAB)

# Save the results
if (savedata){
  ii <- 0
  temp = list()
  for (res in resSummary){
    ii <- ii + 1
    temp[[ii]] <- data.frame(resSummary[ii])
  }
  openxlsx::write.xlsx(temp, paste(outDataPath, "\\PtsABCRFSummary.xlsx",
                                   sep=""),
                       rowNames=TRUE)
}
LS0tDQp0aXRsZTogIlJFRk1BUCBMaXN0ZW5pbmcgdGVzdCAxIFBhcnRzIEEgJiBCIGFuYWx5c2lzOiBSYW5kb20gZm9yZXN0IHZhcmlhYmxlIGltcG9ydGFuY2UgaWRlbnRpZmljYXRpb24gLSBSZXZpc2lvbiAxIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KVGhpcyBub3RlYm9vayBhbmFseXNlcyBib3RoIHBhcnRzIG9mIHRoZSBkYXRhIGluIHRlcm1zIG9mIHZhcmlhYmxlIGltcG9ydGFuY2UsIHVzaW5nIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCBiYXNlZCBvbiBjb25kaXRpb25hbCBpbmZlcmVuY2UgdHJlZXMgYW5kIGEgY29uZGl0aW9uYWwgcGVybXV0YXRpb24gdmFyaWFibGUgaW1wb3J0YW5jZSBhbGdvcml0aG0uDQoNCiMgU2V0dXANCg0KIyMgTG9hZCBwYWNrYWdlcw0KDQpgYGB7cn0NCiMgbG9hZCBwYWNrYWdlcw0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGFydHkpDQpsaWJyYXJ5KGNvbmZsaWN0ZWQpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkob3Blbnhsc3gpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShwZXJtaW1wKQ0KIyBzZXQgcGFja2FnZSBwYXJhbWV0ZXJzDQp0aGVtZV9zZXQodGhlbWVfYncoKSkNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIHBsb3QgY29sb3VyIHNjaGVtZQ0KDQpteWNvbG91cmxpc3QgPSBsaXN0KGMoMCwgMTAyLCAyNTUpLCBjKDAsIDIwNCwgMTUzKSwgYygyNTUsIDAsIDEwMiksIGMoNzQsIDExMSwgMTUyKSwgYygyNTEsIDE2NCwgNDkpLCBjKDIwNCwgMTUzLCAyNTUpLCBjKDkwLCAxOTIsIDI1NSksIGMoODAsIDI0NSwgMjMzKSwgYygyNTUsIDkwLCAxOTIpLCBjKDE2NCwgMjAxLCAyNDIpLCBjKDI1NSwgMjU0LCAxMzkpLCBjKDI1NSwgMjQzLCAyNTUpKQ0KbXljb2xvdXJzID0gbWF0cml4KCkNCg0KZm9yIChpaSBpbiAxOmxlbmd0aChteWNvbG91cmxpc3QpKXsNCiAgbXljb2xvdXJzW2lpXSA9IHJnYihteWNvbG91cmxpc3RbW2lpXV1bMV0vMjU1LA0KICAgICAgICAgICAgICAgICAgICAgIG15Y29sb3VybGlzdFtbaWldXVsyXS8yNTUsDQogICAgICAgICAgICAgICAgICAgICAgbXljb2xvdXJsaXN0W1tpaV1dWzNdLzI1NSkNCn0NCg0KIyB0b2dnbGUgdG8gc2F2ZSBwbG90cw0Kc2F2ZXBsb3RzID0gVFJVRQ0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgIyBzZXQgb3V0cHV0IHBsb3QgZGlyZWN0b3J5DQogIGNob29zZS5maWxlcyhjYXB0aW9uPSJKdXN0IGNhbmNlbCB0aGlzIiwgZmlsdGVycz1tYXRyaXgoZGF0YT1jKCIgIiwgIiAiKSwgbmNvbD0yKSkgICMgd29ya2Fyb3VuZCBmb3IgYnVnIGluIFJUZXJtIGNob29zZS5kaXINCiAgb3V0RmlnUGF0aCA8LSB1dGlsczo6Y2hvb3NlLmRpcihjYXB0aW9uPSJTZWxlY3Qgb3V0cHV0IGZvbGRlciB0byBzYXZlIHBsb3RzICcwMyBFeHBlcmltZW50XFxFeHBlcmltZW50IDFcXEFuYWx5c2lzXFxQbG90cyciKQ0KICANCiAgaWYgKCFkaXIuZXhpc3RzKGZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpKXtkaXIuY3JlYXRlKGZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpfQ0KICBpZiAoIWRpci5leGlzdHMoZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkpe2Rpci5jcmVhdGUoZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSl9DQogIA0KfQ0KDQojIHRvZ2dsZSB0byBzYXZlIGRhdGENCnNhdmVkYXRhID0gVFJVRQ0KDQppZiAoc2F2ZWRhdGEpew0KICAjIHNldCBvdXRwdXQgcGxvdCBkaXJlY3RvcnkNCiAgaWYgKHNhdmVwbG90cz09RkFMU0Upew0KICAgIGNob29zZS5maWxlcyhjYXB0aW9uPSJKdXN0IGNhbmNlbCB0aGlzIiwgZmlsdGVycz1tYXRyaXgoZGF0YT1jKCIgIiwgIiAiKSwgbmNvbD0yKSkgICMgd29ya2Fyb3VuZCBmb3IgYnVnIGluIFJUZXJtIGNob29zZS5kaXINCiAgfQ0KICBvdXREYXRhUGF0aCA8LSB1dGlsczo6Y2hvb3NlLmRpcihjYXB0aW9uPSJTZWxlY3Qgb3V0cHV0IGZvbGRlciB0byBzYXZlIGRhdGEgJzAzIEV4cGVyaW1lbnRcXEV4cGVyaW1lbnQgMVxcQW5hbHlzaXNcXFInIikNCn0NCiANCg0KYGBgDQoNCiMgSW1wb3J0IGRhdGEgYW5kIHdyYW5nbGUNCg0KYGBge3J9DQoNCnN0aW1EYXRhcGF0aCA8LSB1dGlsczo6Y2hvb3NlLmZpbGVzKGNhcHRpb249ciIoU2VsZWN0IHJlZm1hcF9saXN0ZXN0MV90ZXN0ZGF0YV9CeVN0aW0uY3N2IGZyb20gMDMgRXhwZXJpbWVudFxFeHBlcmltZW50IDFcQW5hbHlzaXNcUG9zdFByb2Nlc3MpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJzPW1hdHJpeChkYXRhPWMoInJlZm1hcF9saXN0ZXN0MV90ZXN0ZGF0YV9CeVN0aW0uY3N2IiwgInJlZm1hcF9saXN0ZXN0MV90ZXN0ZGF0YV9CeVN0aW0uY3N2IiksIG5jb2w9MikpDQoNCnN0aW1EYXRhIDwtIHV0aWxzOjpyZWFkLmNzdihzdGltRGF0YXBhdGgsIGhlYWRlcj1UUlVFKQ0KDQpjb2xuYW1lcyhzdGltRGF0YSlbMV0gPC0gIlN0aW11bHVzIg0KDQojIG1ha2UgcmVzcG9uc2UgcHJvcG9ydGlvbnMgaW50byBwZXJjZW50YWdlcw0Kc3RpbURhdGFbWydIaWdoQW5ub3lQYyddXSA8LSBzdGltRGF0YVtbJ0hpZ2hBbm5veVByb3AnXV0qMTAwDQpzdGltRGF0YVtbJ2RIaWdoQW5ub3lQYyddXSA8LSBzdGltRGF0YVtbJ2RIaWdoQW5ub3lQcm9wJ11dKjEwMA0KDQpgYGANCg0KYGBge3J9DQojIGZ1bmN0aW9uIHRvIGVuY29kZSBjYXRlZ29yaWNhbCB0byBvcmRpbmFsIG51bWVyaWMgdmFyaWFibGVzDQplbmNvZGVfb3JkaW5hbCA8LSBmdW5jdGlvbih4LCBvcmRlcj11bmlxdWUoeCkpIHsNCiAgeCA8LSBhcy5udW1lcmljKGZhY3Rvcih4LCBsZXZlbHM9b3JkZXIsIGV4Y2x1ZGU9TlVMTCwgb3JkZXI9VFJVRSkpDQogIHgNCn0NCg0KIyBkZWZpbml0aW9uIG9mIG9yZGluYWwgdmFyaWFibGUgbGV2ZWxzDQpTTlJDYXRzIDwtIGMoIk5vIFVBUyIsICItMTYiLCAiLTEwIiwgIi00IiwgIjIiLCAiOCIpDQpVQVNMQWVxQ2F0cyA8LSBjKCJObyBVQVMiLCAiNDIiLCAiNDgiLCAiNTQiLCAiNjAiKQ0KDQpgYGANCg0KVGhlIGFnZ3JlZ2F0ZWQgZGF0YSBieSBzdGltdWx1cyBhcmUgYXNzaWduZWQgdG8gYSBkYXRhZnJhbWUsIHJlbGV2YW50IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgY29udmVydGVkIHRvIG9yZGluYWwsIGFuZCB0aGVuIHRoZSB2YXJpYWJsZSBzdWJzZXQgb2YgaW50ZXJlc3QgaXMgc2VsZWN0ZWQsIE5BIHJvd3MgZHJvcHBlZCAoaWUsIHRoZSAnbm8gVUFTJyBzdGltdWxpLCBhcyB0aGUgY29uZGl0aW9uYWwgdmFyaWFibGUgaW1wb3J0YW5jZSBhbGdvcml0aG0gY2Fubm90IGN1cnJlbnRseSBoYW5kbGUgTkEgdmFsdWVzLCB3aGljaCBhcmUgcHJlc2VudCBpbiBhbGwgdGhlIFVBUyBkQiBtZXRyaWNzKSwgYW5kIGEgZm9ybXVsYSBhc3NpZ25lZC4NCg0KYGBge3J9DQoNCnN0aW1EYXRhTnVtIDwtIGRhdGEuZnJhbWUoKQ0KDQpzdGltRGF0YU51bSA8LSBjYmluZChzdGltRGF0YVssICdTdGltdWx1cyddLA0KICAgICAgICAgICAgICAgICAgICAgc3RpbURhdGFbLCAiVUFTRXZlbnRzIl0sDQogICAgICAgICAgICAgICAgICAgICBzdGltRGF0YVssIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09IlVBU0xBZXEiKToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2goY29sbmFtZXMoc3RpbURhdGEpPT0iU05SbGV2ZWwiKV0sDQogICAgICAgICAgICAgICAgICAgICBzdGltRGF0YVssIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09IkludGVybWl0UmF0aW9DMk1heExSIik6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09IkludGVybWl0UmF0aW9DNU1heExSIildLA0KICAgICAgICAgICAgICAgICAgICAgc3RpbURhdGFbLCB3aGljaChjb2xuYW1lcyhzdGltRGF0YSk9PSJVQVNMQUVNYXhMUiIpOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaChjb2xuYW1lcyhzdGltRGF0YSk9PSJVQVNFUE5MTWF4TFIiKV0sDQogICAgICAgICAgICAgICAgICAgICBzdGltRGF0YVssIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09IlVBU0xvdWRFQ01BUG93QXZnQmluIik6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09IlVBU0xvdWRJU08zUG93QXZnQmluIildLA0KICAgICAgICAgICAgICAgICAgICAgc3RpbURhdGFbLCB3aGljaChjb2xuYW1lcyhzdGltRGF0YSk9PSJVQVNUb25hbEVDTUFBdmdNYXhMUiIpOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaChjb2xuYW1lcyhzdGltRGF0YSk9PSJVQVNTaGFycHZCSVNPMTA1RXhCaW4iKV0sDQogICAgICAgICAgICAgICAgICAgICBzdGltRGF0YVssIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09IlVBU0ltcHVsc1NITVBvd0F2Z01heExSIik6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09IlVBU1BzeWNoQW5ub3lCb3VjaGVyIildLA0KICAgICAgICAgICAgICAgICAgICAgc3RpbURhdGFbLCB3aGljaChjb2xuYW1lcyhzdGltRGF0YSk9PSJMQWVxTEFGOTBkaWZmIik6DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoKGNvbG5hbWVzKHN0aW1EYXRhKT09ImRQc3ljaEFubm95Qm91Y2hlciIpXSwNCiAgICAgICAgICAgICAgICAgICAgIHN0aW1EYXRhWywgd2hpY2goY29sbmFtZXMoc3RpbURhdGEpPT0iVmFsZW5jZU1lZGlhbiIpOg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaChjb2xuYW1lcyhzdGltRGF0YSk9PSJkSGlnaEFubm95UHJvcCIpXSwNCiAgICAgICAgICAgICAgICAgICAgIHN0aW1EYXRhWywgd2hpY2goY29sbmFtZXMoc3RpbURhdGEpPT0iSGlnaEFubm95UGMiKToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2goY29sbmFtZXMoc3RpbURhdGEpPT0iZEhpZ2hBbm5veVBjIildKQ0KDQojIHJlbW92ZSBkdXBsaWNhdGVkIHZhcmlhYmxlcw0Kc3RpbURhdGFOdW0gPC0gc3Vic2V0KHN0aW1EYXRhTnVtLCBzZWxlY3QgPSAtYyhVQVNMQWVxKSkNCg0KY29sbmFtZXMoc3RpbURhdGFOdW0pWzFdIDwtICJTdGltdWx1cyINCmNvbG5hbWVzKHN0aW1EYXRhTnVtKVsyXSA8LSAiVUFTRXZlbnRzIg0KDQojIG1ha2UgdGhlIGRpc2NyZXRlIG9yZGluYWwgb3V0Y29tZSB2YXJpYWJsZXMgZmFjdG9ycw0Kc3RpbURhdGFOdW1bWydVQVNFdmVudHMnXV0gPC0gZmFjdG9yKHN0aW1EYXRhTnVtW1snVUFTRXZlbnRzJ11dLCBsZXZlbHM9YygwLCAxLCAzLCA1LCA5KSwgb3JkZXI9VFJVRSkNCnN0aW1EYXRhTnVtW1snVmFsZW5jZU1lZGlhbiddXSA8LSBmYWN0b3Ioc3RpbURhdGFOdW1bWydWYWxlbmNlTWVkaWFuJ11dLCBsZXZlbHM9YygxLCAxLjUsIDIsIDIuNSwgMywgMy41LCA0LCA0LjUsIDUpLCBvcmRlcj1UUlVFKQ0Kc3RpbURhdGFOdW1bWydBcm91c2FsTWVkaWFuJ11dIDwtIGZhY3RvcihzdGltRGF0YU51bVtbJ0Fyb3VzYWxNZWRpYW4nXV0sIGxldmVscz1jKDEsIDEuNSwgMiwgMi41LCAzLCAzLjUsIDQsIDQuNSwgNSksIG9yZGVyPVRSVUUpDQpzdGltRGF0YU51bVtbJ0Fubm95TWVkaWFuJ11dIDwtIGZhY3RvcihzdGltRGF0YU51bVtbJ0Fubm95TWVkaWFuJ11dLCBsZXZlbHM9YygwLCAwLjUsIDEsIDEuNSwgMiwgMi41LCAzLCAzLjUsIDQsIDQuNSwgNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNS41LCA2LCA2LjUsIDcsIDcuNSwgOCwgOC41LCA5LCA5LjUsIDEwKSwgb3JkZXI9VFJVRSkNCnN0aW1EYXRhTnVtW1snZFZhbGVuY2VNZWRpYW4nXV0gPC0gZmFjdG9yKHN0aW1EYXRhTnVtW1snZFZhbGVuY2VNZWRpYW4nXV0sIGxldmVscz1jKC00LCAtMy41LCAtMywgLTIuNSwgLTIsIC0xLjUsIC0xLCAtMC41LCAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjUsIDEsIDEuNSwgMiwgMi41LCAzLCAzLjUsICA0KSwgb3JkZXI9VFJVRSkNCnN0aW1EYXRhTnVtW1snZEFyb3VzYWxNZWRpYW4nXV0gPC0gZmFjdG9yKHN0aW1EYXRhTnVtW1snZEFyb3VzYWxNZWRpYW4nXV0sIGxldmVscz1jKC00LCAtMy41LCAtMywgLTIuNSwgLTIsIC0xLjUsIC0xLCAtMC41LCAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjUsIDEsIDEuNSwgMiwgMi41LCAzLCAzLjUsICA0KSwgb3JkZXI9VFJVRSkNCnN0aW1EYXRhTnVtW1snZEFubm95TWVkaWFuJ11dIDwtIGZhY3RvcihzdGltRGF0YU51bVtbJ2RBbm5veU1lZGlhbiddXSwgbGV2ZWxzPWMoLTEwLCAtOS41LCAtOSwgLTguNSwgLTgsIC03LjUsIC03LCAtNi41LCAtNiwgLTUuNSwgLTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLTQuNSwgLTQsIC0zLjUsIC0zLCAtMi41LCAtMiwgLTEuNSwgLTEsIC0wLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLCAwLjUsIDEsIDEuNSwgMiwgMi41LCAzLCAzLjUsIDQsIDQuNSwgNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDUuNSwgNiwgNi41LCA3LCA3LjUsIDgsIDguNSwgOSwgOS41LCAxMCksIG9yZGVyPVRSVUUpDQoNCiMgb21pdCBhbWJpZW50LW9ubHkgc3RpbXVsaQ0Kc3RpbURhdGFOdW0gPC0gc3RpbURhdGFOdW0gfD4gZHBseXI6OmZpbHRlcihVQVNFdmVudHMgIT0gMCkNCg0KDQpzdGltRGF0YU51bSRTTlJsZXZlbCA8LSBhcy5udW1lcmljKHN0aW1EYXRhTnVtJFNOUmxldmVsKQ0KYGBgDQoNCiMgUmFuZG9tIGZvcmVzdCBmdW5jdGlvbnMNCg0KV3JpdGUgYSBmdW5jdGlvbiB0byB0cmFpbiBhIGNvbmRpdGlvbmFsLWluZmVyZW5jZSByYW5kb20gZm9yZXN0IChjcmYpIG1vZGVsIG9uIGlucHV0IGRhdGEgYWNjb3JkaW5nIHRvIGlucHV0IGZvcm11bGEsIGl0ZXJhdGUgb3ZlciBpbnB1dCByYW5kb20gc2VlZHMsIGF2ZXJhZ2UgZXJyb3IgYW5kIHZhcmlhYmxlIGltcG9ydGFuY2UgbWV0cmljcywgYW5kIG91dHB1dCBtZXRyaWNzIHdpdGggcGxvdHRlZA0KDQojIyBBdmVyYWdpbmcgb3ZlciBtdWx0aXBsZSByYW5kb20gc2VlZHMNCg0KYGBge3J9DQoNCm11bHRpX2NyZlJlZyA8LSBmdW5jdGlvbihkYXRhSW4sIGlWYXJzLCBkVmFyLCBzZWVkcywgbnRyZWUsIG10cnksIHBlcm1JbXBDb25kVGhyZXM9MC45NSwgbWluc3BsaXQ9MjAsIG1pbmJ1Y2tldD03LCBucGVybT0xKXsNCiAgIyBpbml0aWFsaXNlIHZhcmlhYmxlcw0KICBjcmZPT0JFcnJBbGwgPC0gMA0KICBjcmZPT0JSTVNFIDwtIDANCiAgY3JmT09CTUFFIDwtIDANCiAgY3JmT09CRXJyUjIgPC0gMA0KICBjcmZNYXJQZXJtSW1wVmFscyA8LSAwDQogIGNyZkNvblBlcm1JbXBWYWxzIDwtIDANCiAgY3JmTWFyUGVybUltcFZhbHNQZXJUcmVlIDwtIGRhdGEuZnJhbWUoKQ0KICBjcmZDb25QZXJtSW1wVmFsc1BlclRyZWUgPC0gZGF0YS5mcmFtZSgpDQogIA0KICBmb3IgKGl0ZXJzIGluIDE6bGVuZ3RoKHNlZWRzKSl7DQogICAgDQogICAgIyBmb3JtdWxhIGZvciByZWdyZXNzaW9uDQogICAgZm9ybVZhcnMgPC0gcmVmb3JtdWxhdGUoaVZhcnMsIGRWYXIpDQogICAgDQogICAgIyBzZXQgcmFuZG9tIHNlZWQNCiAgICBzZXQuc2VlZChzZWVkc1tpdGVyc10pDQogICAgIyB0cmFpbiBjcmYgbW9kZWwNCiAgICBjcmZNb2RlbCA8LSBwYXJ0eTo6Y2ZvcmVzdChmb3JtVmFycywgZGF0YT1kYXRhSW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbHM9cGFydHk6OmNmb3Jlc3RfdW5iaWFzZWQobnRyZWU9bnRyZWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeT1tdHJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbnNwbGl0PW1pbnNwbGl0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmJ1Y2tldD1taW5idWNrZXQpKQ0KICAgIA0KICAgICMgZ2V0IE9PQiBwcmVkaWN0aW9ucw0KICAgIGNyZk1vZGVsT09CIDwtIHByZWRpY3QoY3JmTW9kZWwsIE9PQj1UUlVFLCB0eXBlPSdyZXNwb25zZScpDQogICAgDQogICAgIyBnZXQgT09CIGVycm9yDQogICAgY3JmTW9kZWxPT0JFcnIgPC0gYXMubnVtZXJpYyhhcy5tYXRyaXgoYXMubnVtZXJpYyhhcy5tYXRyaXgoY3JmTW9kZWxPT0IpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0gYXMubnVtZXJpYyhhcy5tYXRyaXgoY3JmTW9kZWxAZGF0YUBlbnYkcmVzcG9uc2VbW25hbWVzKGNyZk1vZGVsQGRhdGFAZW52JHJlc3BvbnNlKV1dKSkpKQ0KDQogICAgIyBPT0IgUk1TRSwgTUFFIGFuZCBSc3F1YXJlZA0KICAgIGNyZk9PQlJNU0UgPC0gY3JmT09CUk1TRSArIHNxcnQobWVhbihjcmZNb2RlbE9PQkVycl4yKSkNCiAgICBjcmZPT0JNQUUgPC0gY3JmT09CTUFFICsgbWVhbihhYnMoY3JmTW9kZWxPT0JFcnIpKQ0KICAgIGNyZk9PQkVyclIyIDwtIGNyZk9PQkVyclIyICsgY29yKGFzLm51bWVyaWMoYXMubWF0cml4KGNyZk1vZGVsT09CKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyhhcy5tYXRyaXgoY3JmTW9kZWxAZGF0YUBlbnYkcmVzcG9uc2VbW25hbWVzKGNyZk1vZGVsQGRhdGFAZW52JHJlc3BvbnNlKV1dKSkpXjINCg0KICAgICMgc2V0IHJhbmRvbSBzZWVkDQogICAgc2V0LnNlZWQoc2VlZHNbaXRlcnNdKQ0KDQogICAgIyBzZXQgcmFuZG9tIHNlZWQNCiAgICBzZXQuc2VlZChzZWVkc1tpdGVyc10pDQogICAgIyBjb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlDQogICAgY3JmQ29uUGVybUltcCA8LSBwZXJtaW1wOjpwZXJtaW1wKGNyZk1vZGVsLCBucGVybT1ucGVybSwgY29uZGl0aW9uYWw9VFJVRSwgdGhyZXNob2xkPXBlcm1JbXBDb25kVGhyZXMsIHByb2dyZXNzQmFyPUZBTFNFKQ0KICAgIA0KICAgIGNyZkNvblBlcm1JbXBWYWxzIDwtIGNyZkNvblBlcm1JbXBWYWxzICsgY3JmQ29uUGVybUltcCR2YWx1ZXMNCiAgICBjcmZDb25QZXJtSW1wVmFsc1BlclRyZWUgPC0gcmJpbmQoY3JmQ29uUGVybUltcFZhbHNQZXJUcmVlLCBjcmZDb25QZXJtSW1wJHBlclRyZWUpDQogIH0NCiAgDQogICMgYXZlcmFnZSBtZXRyaWNzDQogIGNyZk9PQkVyckFsbCA8LSBjcmZPT0JFcnJBbGwvbGVuZ3RoKHNlZWRzKQ0KICBjcmZPT0JSTVNFIDwtIGNyZk9PQlJNU0UvbGVuZ3RoKHNlZWRzKQ0KICBjcmZPT0JNQUUgPC0gY3JmT09CTUFFL2xlbmd0aChzZWVkcykNCiAgY3JmT09CRXJyUjIgPC0gY3JmT09CRXJyUjIvbGVuZ3RoKHNlZWRzKQ0KICBjcmZDb25QZXJtSW1wVmFscyA8LSBkYXRhLmZyYW1lKENvbmRQZXJtSW1wPXNvcnQoY3JmQ29uUGVybUltcFZhbHMvbGVuZ3RoKHNlZWRzKSwgZGVjcmVhc2luZz1UUlVFKSkNCiAgY3JmQ29uUGVybUltcFZhbHNRdGwgPC0gZGF0YS5mcmFtZShhcHBseShjcmZDb25QZXJtSW1wVmFsc1BlclRyZWUsIDIsIHF1YW50aWxlLCBwcm9icz1jKDAuMjUsIDAuNTAsIDAuNzUpKSkNCiAgDQogIHJlc3VsdHNPdXQgPC0gbGlzdCgnT09CX1JNU0UnPWNyZk9PQlJNU0UsICdPT0JfTUFFJz1jcmZPT0JNQUUsICdSc3F1YXJlZCc9Y3JmT09CRXJyUjIsICdjb25kaXRpb25hbF9wZXJtaW1wJz1jcmZDb25QZXJtSW1wVmFscywgICAgICAgICAgICAgICAgICAgICAgJ2NvbmRpdGlvbmFsX3Blcm1pbXBfcGVyVHJlZSc9Y3JmQ29uUGVybUltcFZhbHNQZXJUcmVlLCAnY29uZGl0aW9uYWxfcGVybWltcF9xdGwnPWNyZkNvblBlcm1JbXBWYWxzUXRsKQ0KICByZXR1cm4ocmVzdWx0c091dCkNCn0NCg0KYGBgDQoNCiMjIENvbXBhcmluZyByYW5raW5ncyBmcm9tIHR3byBzZWVkcyANCg0KYGBge3J9DQoNCmNyZlJlZyA8LSBmdW5jdGlvbihkYXRhSW4sIGlWYXJzLCBkVmFyLCBzZWVkcywgbnRyZWUsIG10cnksIHBlcm1JbXBDb25kVGhyZXM9MC45NSwgbWluc3BsaXQ9MjAsIG1pbmJ1Y2tldD03LCBucGVybT0xKXsNCiAgIyBpbml0aWFsaXNlIHZhcmlhYmxlcw0KICBjcmZPT0JFcnJBbGwgPC0gMA0KICBjcmZPT0JSTVNFIDwtIDANCiAgY3JmT09CTUFFIDwtIDANCiAgY3JmT09CRXJyUjIgPC0gMA0KICBjcmZNYXJQZXJtSW1wVmFscyA8LSAwDQogIGNyZkNvblBlcm1JbXBWYWxzIDwtIDANCiAgY3JmTWFyUGVybUltcFZhbHNQZXJUcmVlIDwtIGRhdGEuZnJhbWUoKQ0KICBjcmZDb25QZXJtSW1wVmFsc1BlclRyZWUgPC0gZGF0YS5mcmFtZSgpDQoNCiAgIyBmb3JtdWxhIGZvciByZWdyZXNzaW9uDQogIGZvcm1WYXJzIDwtIHJlZm9ybXVsYXRlKGlWYXJzLCBkVmFyKQ0KICANCiAgZm9yIChpdGVycyBpbiAxOmxlbmd0aChzZWVkcykpew0KICANCiAgICAjIHNldCByYW5kb20gc2VlZA0KICAgIHNldC5zZWVkKHNlZWRzW2l0ZXJzXSkNCiAgICAjIHRyYWluIGNyZiBtb2RlbA0KICAgIGNyZk1vZGVsIDwtIHBhcnR5OjpjZm9yZXN0KGZvcm1WYXJzLCBkYXRhPWRhdGFJbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9scz1wYXJ0eTo6Y2ZvcmVzdF91bmJpYXNlZChudHJlZT1udHJlZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5PW10cnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluc3BsaXQ9bWluc3BsaXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0PW1pbmJ1Y2tldCkpDQogICAgDQogICAgIyBjb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlDQogICAgY3JmQ29uUGVybUltcCA8LSBwZXJtaW1wOjpwZXJtaW1wKGNyZk1vZGVsLCBucGVybT1ucGVybSwgY29uZGl0aW9uYWw9VFJVRSwgdGhyZXNob2xkPXBlcm1JbXBDb25kVGhyZXMsIHByb2dyZXNzQmFyPUZBTFNFKQ0KICAgIA0KICAgIGNyZkNvblBlcm1JbXBWYWxzIDwtIGNyZkNvblBlcm1JbXAkdmFsdWVzDQogICAgDQogICAgaWYgKGl0ZXJzID09IDEpew0KICAgICAgY3JmQ29uUGVybUltcFZhbHMxIDwtIGRhdGEuZnJhbWUoQ29uZFBlcm1JbXA9c29ydChjcmZDb25QZXJtSW1wVmFscywgZGVjcmVhc2luZz1UUlVFKSkNCiAgICAgIGNyZkNvblBlcm1JbXBWYWxzUGVyVHJlZTEgPC0gY3JmQ29uUGVybUltcCRwZXJUcmVlDQogICAgICBjcmZDb25QZXJtSW1wVmFsc1F0bDEgPC0gZGF0YS5mcmFtZShhcHBseShjcmZDb25QZXJtSW1wVmFsc1BlclRyZWUxLCAyLCBxdWFudGlsZSwgcHJvYnM9YygwLjI1LCAwLjUwLCAwLjc1KSkpDQogICAgICANCiAgICAgICMgZ2V0IE9PQiBwcmVkaWN0aW9ucw0KICAgICAgY3JmTW9kZWxPT0IgPC0gcHJlZGljdChjcmZNb2RlbCwgT09CPVRSVUUsIHR5cGU9J3Jlc3BvbnNlJykNCiAgICAgIA0KICAgICAgIyBnZXQgT09CIGVycm9yDQogICAgICBjcmZNb2RlbE9PQkVyciA8LSBhcy5udW1lcmljKGFzLm1hdHJpeChhcy5udW1lcmljKGFzLm1hdHJpeChjcmZNb2RlbE9PQikpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLSBhcy5udW1lcmljKGFzLm1hdHJpeChjcmZNb2RlbEBkYXRhQGVudiRyZXNwb25zZVtbbmFtZXMoY3JmTW9kZWxAZGF0YUBlbnYkcmVzcG9uc2UpXV0pKSkpDQogICAgICANCiAgICAgICMgT09CIFJNU0UsIGVycm9yIHF1YXJ0aWxlcyBhbmQgUnNxdWFyZWQNCiAgICAgIGNyZk9PQlJNU0UgPC0gc3FydChtZWFuKGNyZk1vZGVsT09CRXJyXjIpKQ0KICAgICAgY3JmT09CTUFFIDwtIGNyZk9PQk1BRSArIG1lYW4oYWJzKGNyZk1vZGVsT09CRXJyKSkNCiAgICAgIGNyZk9PQkVyclIyIDwtIGNvcihhcy5udW1lcmljKGFzLm1hdHJpeChjcmZNb2RlbE9PQikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyhhcy5tYXRyaXgoY3JmTW9kZWxAZGF0YUBlbnYkcmVzcG9uc2VbW25hbWVzKGNyZk1vZGVsQGRhdGFAZW52JHJlc3BvbnNlKV1dKSkpXjINCg0KICAgICAgfQ0KICAgIA0KICAgIGVsc2V7DQogICAgICBjcmZDb25QZXJtSW1wVmFsc04gPC0gZGF0YS5mcmFtZShDb25kUGVybUltcD1zb3J0KGNyZkNvblBlcm1JbXBWYWxzLCBkZWNyZWFzaW5nPVRSVUUpKQ0KICAgICAgDQogICAgICBuVmFySW1wQ2hlY2tzIDwtIG1pbihtYXgoc3VtKGNyZkNvblBlcm1JbXBWYWxzMSA+PSBjcmZDb25QZXJtSW1wVmFsczEkQ29uZFBlcm1JbXBbMV0qMC4xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oY3JmQ29uUGVybUltcFZhbHNOID49IGNyZkNvblBlcm1JbXBWYWxzTiRDb25kUGVybUltcFsxXSowLjEpKSwgNCkgICMgbnVtYmVyIG9mIHZhcmlhYmxlIGltcG9ydGFuY2UgdmFsdWVzIHdpdGggYSB2YWx1ZSBhdCBsZWFzdCAxMCUgb2YgdGhlIGhpZ2hlc3QgaW1wb3J0YW5jZQ0KICAgICAgaWYgKHN1bShyb3duYW1lcyhjcmZDb25QZXJtSW1wVmFsczEpWzE6blZhckltcENoZWNrc10gIT0gcm93bmFtZXMoY3JmQ29uUGVybUltcFZhbHNOKVsxOm5WYXJJbXBDaGVja3NdKSA+IDApew0KICAgICAgICB3YXJuaW5nKCJQZXJtdXRhdGlvbiBpbXBvcnRhbmNlIHJhbmsgb3JkZXIgd2l0aGluIDEwJSBvZiBtYXggZGlmZmVycyBiZXR3ZWVuIHNlZWRzOiBpbmNyZWFzZSBudW1iZXIgb2YgdHJlZXMgKCdudHJlZScpIG9yIG51bWJlciBvZiBwZXJtdXRhdGlvbnMgKCducGVybScpLCBvciBzdWJzYW1wbGUgb2YgZmVhdHVyZXMgKCdtdHJ5JykiKQ0KICAgICAgfQ0KICAgICAgZWxzZXsNCiAgICAgICAgcmVzdWx0c091dCA8LSBsaXN0KCdPT0JfZXJyb3JzJz1jcmZNb2RlbE9PQkVyciwgJ09PQl9STVNFJz1jcmZPT0JSTVNFLCAnT09CX01BRSc9Y3JmT09CTUFFLCAnUnNxdWFyZWQnPWNyZk9PQkVyclIyLCAnY29uZGl0aW9uYWxfcGVybWltcCc9Y3JmQ29uUGVybUltcFZhbHMxLCAnY29uZGl0aW9uYWxfcGVybWltcF9wZXJUcmVlJz1jcmZDb25QZXJtSW1wVmFsc1BlclRyZWUxLCAnY29uZGl0aW9uYWxfcGVybXBpbXBfcXRsJz1jcmZDb25QZXJtSW1wVmFsc1F0bDEpDQogICAgICAgIHJldHVybihyZXN1bHRzT3V0KQ0KICAgICAgfQ0KICAgICAgDQogICAgfQ0KICAgIA0KICB9DQoNCn0NCg0KYGBgDQoNCiMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQptdHJ5VHVuZSA8LSBmdW5jdGlvbihkYXRhSW4sIGlWYXJzLCBkVmFyLCBzZWVkcywgbnRyZWVzLCBtaW5zcGxpdD0yMCwgbWluYnVja2V0PTcpew0KDQogIGZvcm1WYXJzIDwtIHJlZm9ybXVsYXRlKGlWYXJzLCBkVmFyKQ0KICANCiAgIyBzZXQgbXRyeSB2YWx1ZXMgYW5kIGNvcnJlc3BvbmRpbmcgaVZhcnMvbXRyeSByYXRpb3MNCiAgaWYgKGxlbmd0aChpVmFycykgPiA5KXsNCiAgICBpVmFyc19tdHJ5cyA8LSBjKDEwLjUsIDUuMjUsIDMuNSwgMi43NSwgMi4yNSwgMS43NSwgMS41LCAxLjI1KQ0KICAgIG10cnlzIDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS9pVmFyc19tdHJ5cykNCiAgfQ0KICBlbHNlew0KICAgIG10cnlzIDwtIHNlcSgyLCBsZW5ndGgoaVZhcnMpIC0gMywgYnk9MSkNCiAgICBpVmFyc19tdHJ5cyA8LSBsZW5ndGgoaVZhcnMpL210cnlzDQogIH0NCiAgaVZhcnNfbXRyeXMgPC0gaVZhcnNfbXRyeXNbbXRyeXMgPj0gMl0gICMgcmVtb3ZlIDAgb3IgMSB2YWx1ZXMNCiAgbXRyeXMgPC0gbXRyeXNbbXRyeXMgPj0gMl0gICMgcmVtb3ZlIDAgb3IgMSB2YWx1ZXMNCiAgDQogICMgcmVtb3ZlIGFueSBkdXBsaWNhdGVkIHZhbHVlcw0KICBpVmFyc19tdHJ5cyA8LSBpVmFyc19tdHJ5c1shKGR1cGxpY2F0ZWQobXRyeXMpIHwgZHVwbGljYXRlZChtdHJ5cywgZnJvbUxhc3QgPSBUUlVFKSldDQogIG10cnlzIDwtIG10cnlzWyEoZHVwbGljYXRlZChtdHJ5cykgfCBkdXBsaWNhdGVkKG10cnlzLCBmcm9tTGFzdCA9IFRSVUUpKV0NCg0KICAjIGVuc3VyZSBtdHJ5cyBpcyBsZXNzIHRoYW4gbGVuZ3RoKGlWYXJzKQ0KICBpVmFyc19tdHJ5cyA8LSBpVmFyc19tdHJ5c1ttdHJ5cyA8IGxlbmd0aChpVmFycyldDQogIG10cnlzIDwtIG10cnlzW210cnlzIDwgbGVuZ3RoKGlWYXJzKV0NCg0KICByZXNSTVNFTWFwID0gbWF0cml4KGRhdGE9MCwgbnJvdz1sZW5ndGgobXRyeXMpLCBuY29sPWxlbmd0aChudHJlZXMpKQ0KICByZXNSc3F1YXJlZE1hcCA9IG1hdHJpeChkYXRhPTAsIG5yb3c9bGVuZ3RoKG10cnlzKSwgbmNvbD1sZW5ndGgobnRyZWVzKSkNCiAgcmVzTUFFTWFwID0gbWF0cml4KGRhdGE9MCwgbnJvdz1sZW5ndGgobXRyeXMpLCBuY29sPWxlbmd0aChudHJlZXMpKQ0KICANCiAgDQogIGZvciAoaWkgaW4gc2VxKDEsIGxlbmd0aChudHJlZXMpKSl7DQogICAgDQogICAgdHVuZU1vZC5yZXN1bHRzIDwtIGRhdGEuZnJhbWUoUk1TRT1udW1lcmljKGxlbmd0aChtdHJ5cykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSc3F1YXJlZD1udW1lcmljKGxlbmd0aChtdHJ5cykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNQUU9bnVtZXJpYyhsZW5ndGgobXRyeXMpKSkNCiAgICANCiAgICBmb3IgKHNlZWQgaW4gc2VlZHMpew0KICAgICAgc2V0LnNlZWQoc2VlZCkNCiAgICAgIG50cmVlID0gbnRyZWVzW2lpXQ0KICAgICAgdHVuZU1vZCA8LSBjYXJldDo6dHJhaW4oZm9ybVZhcnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWRhdGFJbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZD0nY2ZvcmVzdCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9scz1wYXJ0eTo6Y2ZvcmVzdF91bmJpYXNlZChudHJlZT1udHJlZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbnNwbGl0PW1pbnNwbGl0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0PW1pbmJ1Y2tldCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZD1kYXRhLmZyYW1lKC5tdHJ5PW10cnlzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAib29iIikpDQogICAgICANCiAgICAgIA0KICAgICAgDQogICAgICAjIGFjY3VtdWxhdGUgcmVzdWx0cw0KICAgICAgcmVzUk1TRU1hcFssIGlpXSA8LSByZXNSTVNFTWFwWywgaWldICsgdHVuZU1vZCRyZXN1bHRzJFJNU0UNCiAgICAgIHJlc1JzcXVhcmVkTWFwWywgaWldIDwtIHJlc1JzcXVhcmVkTWFwWywgaWldICsgdHVuZU1vZCRyZXN1bHRzJFJzcXVhcmVkDQogICAgICByZXNNQUVNYXBbLCBpaV0gPC0gcmVzTUFFTWFwWywgaWldICsgdHVuZU1vZCRyZXN1bHRzJE1BRQ0KICAgICAgDQogICAgICB0dW5lTW9kLnJlc3VsdHMgPC0gdHVuZU1vZC5yZXN1bHRzICsgdHVuZU1vZCRyZXN1bHRzWywgd2hpY2gobmFtZXModHVuZU1vZCRyZXN1bHRzKSAhPSAnbXRyeScpXQ0KICAgIH0NCg0KICAgICMgYXZlcmFnZSByZXN1bHRzDQogICAgdHVuZU1vZC5yZXN1bHRzIDwtIHR1bmVNb2QucmVzdWx0cy9sZW5ndGgoc2VlZHMpDQogICAgdHVuZU1vZC5yZXN1bHRzIDwtIGNiaW5kKHR1bmVNb2QucmVzdWx0cywgZGF0YS5mcmFtZShtdHJ5PW10cnlzKSwgZGF0YS5mcmFtZShpVmFyc19tdHJ5PWlWYXJzX210cnlzKSkNCg0KICAgIHByaW50KHR1bmVNb2QucmVzdWx0cykNCg0KICB9DQogIA0KICAjIGF2ZXJhZ2UgcmVzdWx0cw0KICByZXNSTVNFTWFwIDwtIHJlc1JNU0VNYXAvbGVuZ3RoKHNlZWRzKQ0KICByZXNSc3F1YXJlZE1hcCA8LSByZXNSc3F1YXJlZE1hcC9sZW5ndGgoc2VlZHMpDQogIHJlc01BRU1hcCA8LSByZXNNQUVNYXAvbGVuZ3RoKHNlZWRzKQ0KICANCiAgDQogICMgY29udmVydCB0byBkYXRhIGZyYW1lcyB3aXRoIG10cnkgYXMgcm93IG5hbWVzIGFuZCBudHJlZSBhcyBjb2x1bW4gbmFtZXMsIGFuZCBjb252ZXJ0IHRvIGxvbmcgZm9ybWF0IHVzaW5nIHRpZHl2ZXJzZQ0KICByZXNkZlJNU0VNYXAgPC0gYXMuZGF0YS5mcmFtZShyZXNSTVNFTWFwKQ0KICByb3duYW1lcyhyZXNkZlJNU0VNYXApIDwtIG10cnlzDQogIGNvbG5hbWVzKHJlc2RmUk1TRU1hcCkgPC0gbnRyZWVzDQogIHJlc2RmUnNxdWFyZWRNYXAgPC0gYXMuZGF0YS5mcmFtZShyZXNSc3F1YXJlZE1hcCkNCiAgcm93bmFtZXMocmVzZGZSc3F1YXJlZE1hcCkgPC0gbXRyeXMNCiAgY29sbmFtZXMocmVzZGZSc3F1YXJlZE1hcCkgPC0gbnRyZWVzDQogIHJlc2RmTUFFTWFwIDwtIGFzLmRhdGEuZnJhbWUocmVzTUFFTWFwKQ0KICByb3duYW1lcyhyZXNkZk1BRU1hcCkgPC0gbXRyeXMNCiAgY29sbmFtZXMocmVzZGZNQUVNYXApIDwtIG50cmVlcw0KICANCiAgDQogICMgY29udmVydCBkYXRhZnJhbWVzIHRvIGxvbmcgZm9ybWF0IHVzaW5nIHRpZHl2ZXJzZQ0KICByZXNkZlJNU0VNYXAgPC0gcmVzZGZSTVNFTWFwIHw+DQogICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCdtdHJ5JykgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2F0aGVyKGtleT0nbnRyZWUnLCB2YWx1ZT0nUk1TRScsIC1tdHJ5KQ0KICANCiAgcmVzZGZSc3F1YXJlZE1hcCA8LSByZXNkZlJzcXVhcmVkTWFwIHw+DQogICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbignbXRyeScpIHw+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnYXRoZXIoa2V5PSdudHJlZScsIHZhbHVlPSdSc3F1YXJlZCcsIC1tdHJ5KQ0KICANCiAgcmVzZGZNQUVNYXAgPC0gcmVzZGZNQUVNYXAgfD4NCiAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCdtdHJ5JykgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgIGdhdGhlcihrZXk9J250cmVlJywgdmFsdWU9J01BRScsIC1tdHJ5KQ0KICANCiAgIyBlbnN1cmUgbnRyZWUgYW5kIG10cnkgY29sdW1ucyBhcmUgb3JkZXJlZCBmYWN0b3JzDQogIHJlc2RmUk1TRU1hcCRudHJlZSA8LSBmYWN0b3IocmVzZGZSTVNFTWFwJG50cmVlLCBsZXZlbHM9YXMuY2hhcmFjdGVyKG50cmVlcykpDQogIHJlc2RmUk1TRU1hcCRtdHJ5IDwtIGZhY3RvcihyZXNkZlJNU0VNYXAkbXRyeSwgbGV2ZWxzPWFzLmNoYXJhY3RlcihtdHJ5cykpDQogIA0KICByZXNkZlJzcXVhcmVkTWFwJG50cmVlIDwtIGZhY3RvcihyZXNkZlJzcXVhcmVkTWFwJG50cmVlLCBsZXZlbHM9YXMuY2hhcmFjdGVyKG50cmVlcykpDQogIHJlc2RmUnNxdWFyZWRNYXAkbXRyeSA8LSBmYWN0b3IocmVzZGZSc3F1YXJlZE1hcCRtdHJ5LCBsZXZlbHM9YXMuY2hhcmFjdGVyKG10cnlzKSkNCiAgDQogIHJlc2RmTUFFTWFwJG50cmVlIDwtIGZhY3RvcihyZXNkZk1BRU1hcCRudHJlZSwgbGV2ZWxzPWFzLmNoYXJhY3RlcihudHJlZXMpKQ0KICByZXNkZk1BRU1hcCRtdHJ5IDwtIGZhY3RvcihyZXNkZk1BRU1hcCRtdHJ5LCBsZXZlbHM9YXMuY2hhcmFjdGVyKG10cnlzKSkNCiAgDQogICMgcGxvdCBoZWF0bWFwcyB1c2luZyBnZ3Bsb3QsIHdpdGggZXh0cmVtZSAobWluIG9yIG1heCkgdmFsdWUgcGxvdHRlZCBhcyBvdmVybGFpZCBwb2ludCB1c2luZyBhbm5vdGF0ZSBhbmQgY29sb3VyYmFyIHNjYWxlIHJldmVyc2VkDQogIHBIZWF0bWFwUk1TRSA8LSBnZ3Bsb3QocmVzZGZSTVNFTWFwKSArDQogICAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShhZXMoeD1udHJlZSwgeT1tdHJ5LCBmaWxsPVJNU0UpKSArDQogICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uPSJ2aXJpZGlzIiwgZGlyZWN0aW9uPS0xKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoZGF0YT1yZXNkZlJNU0VNYXBbd2hpY2gocmVzZGZSTVNFTWFwJFJNU0UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9PSBtaW4ocmVzZGZSTVNFTWFwJFJNU0UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFyci5pbmQgPSBUUlVFKSxdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4PW50cmVlLCB5PW10cnkpLCBjb2xvdXI9InJlZCIsIHNpemU9MikgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9jb2xvdXJiYXIocmV2ZXJzZT1UUlVFKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFicyh4PSJudHJlZSIsIHk9Im10cnkiLCBmaWxsPSJSTVNFIikgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpKQ0KICANCiAgcEhlYXRtYXBSc3F1YXJlZCA8LSBnZ3Bsb3QocmVzZGZSc3F1YXJlZE1hcCkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKGFlcyh4PW50cmVlLCB5PW10cnksIGZpbGw9UnNxdWFyZWQpKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0idmlyaWRpcyIsIGRpcmVjdGlvbj0xKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGRhdGE9cmVzZGZSc3F1YXJlZE1hcFt3aGljaChyZXNkZlJzcXVhcmVkTWFwJFJzcXVhcmVkDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9PSBtYXgocmVzZGZSc3F1YXJlZE1hcCRSc3F1YXJlZCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcnIuaW5kID0gVFJVRSksXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHg9bnRyZWUsIHk9bXRyeSksIGNvbG91cj0icmVkIiwgc2l6ZT0yKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9jb2xvdXJiYXIocmV2ZXJzZT1UUlVFKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYnMoeD0ibnRyZWUiLCB5PSJtdHJ5IiwgZmlsbD0iUnNxdWFyZWQiKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpKQ0KICANCiAgcEhlYXRtYXBNQUUgPC0gZ2dwbG90KHJlc2RmTUFFTWFwKSArDQogICAgICAgICAgICAgICAgICAgIGdlb21fdGlsZShhZXMoeD1udHJlZSwgeT1tdHJ5LCBmaWxsPU1BRSkpICsNCiAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb249InZpcmlkaXMiLCBkaXJlY3Rpb249LTEpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChkYXRhPXJlc2RmTUFFTWFwW3doaWNoKHJlc2RmTUFFTWFwJE1BRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPT0gbWluKHJlc2RmTUFFTWFwJE1BRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcnIuaW5kID0gVFJVRSksXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeD1udHJlZSwgeT1tdHJ5KSwgY29sb3VyPSJyZWQiLCBzaXplPTIpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfY29sb3VyYmFyKHJldmVyc2U9VFJVRSkpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYnMoeD0ibnRyZWUiLCB5PSJtdHJ5IiwgZmlsbD0iTUFFIikgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpKQ0KICANCiAgcCA8LSAgY293cGxvdDo6cGxvdF9ncmlkKHBIZWF0bWFwUk1TRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBIZWF0bWFwUnNxdWFyZWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwSGVhdG1hcE1BRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2w9MywgbnJvdz0xKQ0KICANCiAgcmV0dXJuKHApDQoNCn0gICMgZW5kIG9mIGZ1bmN0aW9uDQoNCmBgYA0KDQoNCiMgUGFydHMgQSAmIEIgYW5hbHlzaXMNCg0KDQojIyBTZXQgZ2xvYmFsIHBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCnBlcm1JbXBDb25kVGhyZXMgPC0gMC45NQ0KbWluc3BsaXQgPC0gMjANCm1pbmJ1Y2tldCA8LSA3DQpudHJlZXMgPC0gYygyNTEsIDUwMSwgMTAwMSwgMTUwMSwgMjUwMSwgNDAwMSwgNTUwMSkNCg0KZXZlbnRWYXIgPC0gIlVBU0V2ZW50cyINCmFtYlZhciA8LSAiQW1iaWVudExBZXEiDQoNCmBgYA0KDQojIyBNZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UNCg0KIyMjIEluaXRpYWxpc2UgcmVzdWx0cyBvdXRwdXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KcmVzZEFubm95TW5GaXRBQiA8LSBkYXRhLmZyYW1lKFJNU0UgPSBudW1lcmljKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTUFFID0gbnVtZXJpYygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJzcXVhcmVkID0gbnVtZXJpYygpKQ0KcmVzZEFubm95TW5QZXJtSW1wQUIgPC0gbGlzdCgpDQoNCmBgYA0KDQoNCiMjIyBBYnNvbHV0ZSB2YXJpYWJsZXMNCg0KIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBuYW1lcyhzdGltRGF0YU51bSlbd2hpY2gobmFtZXMoc3RpbURhdGFOdW0pID09ICdVQVNFdmVudHMnKTp3aGljaChuYW1lcyhzdGltRGF0YU51bSkgPT0gJ1VBU1BzeWNoQW5ub3lCb3VjaGVyJyldDQppVmFycyA8LSBpVmFyc1shIGlWYXJzICVpbiUgYygnU05SbGV2ZWwnLCAnSW50ZXJtaXRSYXRpb0MyTWF4TFInLCAnSW50ZXJtaXRSYXRpb0MzTWF4TFInLCAnSW50ZXJtaXRSYXRpb0M1TWF4TFInKV0NCg0KZFZhciA8LSAiZEFubm95TWVhbiINCg0Kc2VlZHMgPC0gYyg1NzgzMTIsIDU0NCwgODQ4OTQsIDU0NjU0LCAxNTMxNTcpDQoNCmBgYA0KDQojIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFic1ZhcnNIeXBlclR1bmUuc3ZnIiwgd2lkdGg9MTIsIGhlaWdodD00LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFic1ZhcnNIeXBlclR1bmUuc3ZnIikNCg0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BYnNWYXJzSHlwZXJUdW5lLnBkZiIsIHdpZHRoPTEyLCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BYnNWYXJzSHlwZXJUdW5lLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDI1MDENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzEuNzUpDQoNCmBgYA0KDQojIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0QWJzIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dEFicyRPT0JfUk1TRQ0KcmVzdWx0c091dEFicyRPT0JfTUFFDQpyZXN1bHRzT3V0QWJzJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dEFicyA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0QWJzJE9PQl9STVNFDQpyZXN1bHRzT3V0QWJzJE9PQl9NQUUNCnJlc3VsdHNPdXRBYnMkUnNxdWFyZWQNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWJzIHZhcnMnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRBYnMkT09CX1JNU0UNCnJlc2RBbm5veU1uRml0QUJbJ0FicyB2YXJzJywgJ01BRSddIDwtIHJlc3VsdHNPdXRBYnMkT09CX01BRQ0KcmVzZEFubm95TW5GaXRBQlsnQWJzIHZhcnMnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0QWJzJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNWYXJzIDwtIHJlc3VsdHNPdXRBYnMkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9MTN9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0QWJzLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRBYnMkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0QWJzLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEFicy5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dEFicy5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzFdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsNCiAgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BYnNWYXJzQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9MTMsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWJzVmFyc0NvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFic1ZhcnNDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0xMywgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BYnNWYXJzQ29uUGVybWltcC5wZGYiKQ0KfQ0KYGBgDQoNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9MTB9DQoNCiMgUGxvdCBvbmx5IHBvc2l0aXZlIHZhbHVlcw0KDQpyZXN1bHRzT3V0QWJzLmNvbmltcFB0diA8LSByZXN1bHRzT3V0QWJzLmNvbmltcCB8Pg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCdNZXRyaWMnKSB8Pg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyX2lmKGlzLm51bWVyaWMsIGFsbF92YXJzKC4gPiAwKSkgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygnTWV0cmljJykNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dEFicy5jb25pbXBQdHYpICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRBYnMuY29uaW1wUHR2KSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRBYnMuY29uaW1wUHR2KSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1sxXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWJzVmFyc0NvblBlcm1pbXBQdHYuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTEwLCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFic1ZhcnNDb25QZXJtaW1wUHR2LnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BYnNWYXJzQ29uUGVybWltcFB0di5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9MTAsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWJzVmFyc0NvblBlcm1pbXBQdHYucGRmIikNCn0NCg0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTN9DQoNCiMgUGxvdCBvbmx5IHZhbHVlcyB3aXRoaW4gMSUgb2YgdGhlIG1heGltdW0NCg0KcmVzdWx0c091dEFicy5jb25pbXAxcGMgPC0gcmVzdWx0c091dEFicy5jb25pbXAgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbignTWV0cmljJykgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcl9pZihpcy5udW1lcmljLCBhbGxfdmFycyguID4gbWF4KHJlc3VsdHNPdXRBYnMuY29uaW1wKS8xMDApKSB8Pg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCdNZXRyaWMnKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0QWJzLmNvbmltcDFwYykgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEFicy5jb25pbXAxcGMpLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dEFicy5jb25pbXAxcGMpKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzFdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BYnNWYXJzQ29uUGVybWltcDFwYy5zdmciLCB3aWR0aD04LCBoZWlnaHQ9MywgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BYnNWYXJzQ29uUGVybWltcDFwYy5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWJzVmFyc0NvblBlcm1pbXAxcGMucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTMsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWJzVmFyc0NvblBlcm1pbXAxcGMucGRmIikNCn0NCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0KYWJzVmFyIDwtICJVQVNMQUVNYXhMUiINCg0KYGBgDQoNCg0KIyMjIFNRTSBhbmFseXNpcw0KDQojIyMjIEluZGl2aWR1YWwgU1FNcw0KDQojIyMjIyBTaGFycG5lc3MNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYWJzVmFyLCBldmVudFZhciwgYW1iVmFyLCAiVUFTU2hhcnBBdXJJU08zUG93QXZnQmluIiwgIlVBU1NoYXJwQXVySVNPMzA1RXhCaW4iLCAiVUFTU2hhcnBBdXJTSE1Qb3dBdmdCaW4iLCAiVUFTU2hhcnBBdXJTSE0wNUV4QmluIiwgIlVBU1NoYXJwQXVySVNPMVBvd0F2Z0JpbiIsICJVQVNTaGFycEF1cklTTzEwNUV4QmluIiwgIlVBU1NoYXJwdkJJU08xUG93QXZnQmluIiwgIlVBU1NoYXJwdkJJU08xMDVFeEJpbiIsICJVQVNTaGFycERJTlBvd0F2Z0JpbiIsICJVQVNTaGFycERJTjA1RXhCaW4iLCAiVUFTU2hhcnBBdXJJU08xTWVkQmluIiwNCiAgICAgICAgICJVQVNUb25TaHBBdXJTSE1Qb3dBdmdCaW4iLCAiVUFTVG9uU2hwQXVyU0hNMDVFeEJpbiIpDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDcwNDEsIDkwNSwgNDk4NDY1MSwgNjUxMzIxMywgMTIwNjUxKQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAyNTENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzEuNSkNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFNoYXJwIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNoYXJwJE9PQl9STVNFDQpyZXN1bHRzT3V0U2hhcnAkT09CX01BRQ0KcmVzdWx0c091dFNoYXJwJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNoYXJwIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTaGFycCRPT0JfUk1TRQ0KcmVzdWx0c091dFNoYXJwJE9PQl9NQUUNCnJlc3VsdHNPdXRTaGFycCRSc3F1YXJlZA0KDQpgYGANCg0KDQpgYGB7cn0NCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWJzIHNoYXJwJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0U2hhcnAkT09CX1JNU0UNCnJlc2RBbm5veU1uRml0QUJbJ0FicyBzaGFycCcsICdNQUUnXSA8LSByZXN1bHRzT3V0U2hhcnAkT09CX01BRQ0KcmVzZEFubm95TW5GaXRBQlsnQWJzIHNoYXJwJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFNoYXJwJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNTaGFycCA8LSByZXN1bHRzT3V0U2hhcnAkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD00Ljl9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0U2hhcnAuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFNoYXJwJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFNoYXJwLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFNoYXJwLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0U2hhcnAuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1syXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyBnZ3RpdGxlKCJTaGFycG5lc3MiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5TaGFycENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTQuOSwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5TaGFycENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNblNoYXJwQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NC45LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNblNoYXJwQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCnNoYXJwVmFyIDwtICJVQVNTaGFycEF1cklTTzMwNUV4QmluIg0KDQpgYGANCg0KIyMjIyMgVG9uYWwgbG91ZG5lc3MgYW5kIHRvbmFsaXR5DQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFic1ZhciwgZXZlbnRWYXIsIGFtYlZhciwgIlVBU1RvbmFsRUNNQUF2Z01heExSIiwgIlVBU1RvbmFsU0hNSW50MDVFeE1heExSIiwgIlVBU1RvbmFsU0hNSW50QXZnTWF4TFIiLCAiVUFTVG9uYWxFQ01BMDVFeE1heExSIiwgIlVBU1RvbmFsQXdTSE1BdmdNYXhMUiIsCSJVQVNUb25hbEF3U0hNMDVFeE1heExSIiwJIlVBU1RvbmFsQXdTSE1JbnRBdmdNYXhMUiIsIAkiVUFTVG9uYWxBd1NITUludDA1RXhNYXhMUiIsICJVQVNUb25MZEVDTUFQb3dBdmdCaW4iLCAiVUFTVG9uTGRFQ01BMDVFeEJpbiIsICJVQVNUb25hbEF1ckF2Z01heExSIiwgIlVBU1RvbmFsQXVyMDVFeE1heExSIiwgIlVBU1RvbmFsQXVyMTBFeE1heExSIiwNCiAgICAgICAgICJVQVNUb25TaHBBdXJTSE1Qb3dBdmdCaW4iLCAiVUFTVG9uU2hwQXVyU0hNMDVFeEJpbiIpDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDU0MCwgMTA0Nzk4LCA0NTY0NjQsIDg3MzMxLCA5NDU2NCkNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMjUxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjUpDQoNCmBgYA0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eSB3aXRoIHRvbmFsIGxvdWRuZXNzDQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFRvbmFsMSA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRUb25hbDEkT09CX1JNU0UNCnJlc3VsdHNPdXRUb25hbDEkT09CX01BRQ0KcmVzdWx0c091dFRvbmFsMSRSc3F1YXJlZA0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eSB3aXRoIHRvbmFsIGxvdWRuZXNzDQoNCnJlc3VsdHNPdXRUb25hbDEgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFRvbmFsMSRPT0JfUk1TRQ0KcmVzdWx0c091dFRvbmFsMSRPT0JfTUFFDQpyZXN1bHRzT3V0VG9uYWwxJFJzcXVhcmVkDQoNCmBgYA0KDQpgYGB7cn0NCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWJzIHRvbmFsIGluYyBsb3VkJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0VG9uYWwxJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgdG9uYWwgaW5jIGxvdWQnLCAnTUFFJ10gPC0gcmVzdWx0c091dFRvbmFsMSRPT0JfTUFFDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgdG9uYWwgaW5jIGxvdWQnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0VG9uYWwxJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNUb25hbDEgPC0gcmVzdWx0c091dFRvbmFsMSRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTV9DQoNCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRUb25hbDEuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFRvbmFsMSRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRUb25hbDEuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0VG9uYWwxLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0VG9uYWwxLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbM10sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAobWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlKSIpICsgZ2d0aXRsZSgiVG9uYWxpdHkgaW5jLiB0b25hbCBsb3VkbmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCAxLjQpKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uVG9uYWxMZENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTUsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uVG9uYWxMZENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNblRvbmFsTGRDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD01LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNblRvbmFsTGRDb25QZXJtaW1wLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCnRvbkxkVmFyIDwtICJVQVNUb25MZEVDTUFQb3dBdmdCaW4iDQoNCmBgYA0KDQojIyMjIyBUb25hbGl0eSB3aXRob3V0IHRvbmFsIGxvdWRuZXNzIG9yIHRvbmFsIHNoYXJwbmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhYnNWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJVQVNUb25hbEVDTUFBdmdNYXhMUiIsICJVQVNUb25hbFNITUludDA1RXhNYXhMUiIsICJVQVNUb25hbFNITUludEF2Z01heExSIiwgIlVBU1RvbmFsRUNNQTA1RXhNYXhMUiIsICJVQVNUb25hbEF3U0hNQXZnTWF4TFIiLAkiVUFTVG9uYWxBd1NITTA1RXhNYXhMUiIsCSJVQVNUb25hbEF3U0hNSW50QXZnTWF4TFIiLCAiVUFTVG9uYWxBd1NITUludDA1RXhNYXhMUiIsCSJVQVNUb25hbEF1ckF2Z01heExSIiwgIlVBU1RvbmFsQXVyMDVFeE1heExSIiwgIlVBU1RvbmFsQXVyMTBFeE1heExSIikNCmRWYXIgPC0gImRBbm5veU1lYW4iDQoNCnNlZWRzIDwtIGMoMTU2MDg5LCA1ODYwLCAxMDUyOCwgODk1NDEsIDQ2ODUxNDYpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDUwMQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS4yNSkNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQojIFRvbmFsaXR5DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFRvbmFsMiA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRUb25hbDIkT09CX1JNU0UNCnJlc3VsdHNPdXRUb25hbDIkT09CX01BRQ0KcmVzdWx0c091dFRvbmFsMiRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQojIFRvbmFsaXR5DQoNCnJlc3VsdHNPdXRUb25hbDIgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFRvbmFsMiRPT0JfUk1TRQ0KcmVzdWx0c091dFRvbmFsMiRPT0JfTUFFDQpyZXN1bHRzT3V0VG9uYWwyJFJzcXVhcmVkDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RBbm5veU1uRml0QUJbJ0FicyB0b25hbCBubyBsb3VkJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0VG9uYWwyJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgdG9uYWwgbm8gbG91ZCcsICdNQUUnXSA8LSByZXN1bHRzT3V0VG9uYWwyJE9PQl9NQUUNCnJlc2RBbm5veU1uRml0QUJbJ0FicyB0b25hbCBubyBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFRvbmFsMiRSc3F1YXJlZA0KcmVzZEFubm95TW5QZXJtSW1wQUIkQWJzVG9uYWwyIDwtIHJlc3VsdHNPdXRUb25hbDIkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD00LjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0VG9uYWwyLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRUb25hbDIkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0VG9uYWwyLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFRvbmFsMi5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dFRvbmFsMi5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzNdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIGdndGl0bGUoIlRvbmFsaXR5IHcvbyB0b25hbCBsb3VkbmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCAxLjQpKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uVG9uYWxDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD00LjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uVG9uYWxDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5Ub25hbENvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTQuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5Ub25hbENvblBlcm1pbXAucGRmIikNCn0NCg0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQp0b25hbFZhciA8LSAiVUFTVG9uYWxBd1NITUludDA1RXhNYXhMUiINCg0KYGBgDQoNCiMjIyMjIEZsdWN0dWF0aW9uIHN0cmVuZ3RoDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQojIEZsdWN0dWF0aW9uIHN0cmVuZ3RoDQppVmFycyA8LSBjKGFic1ZhciwgZXZlbnRWYXIsIGFtYlZhciwgIlVBU0ZsdWN0T2xkU0hNMTBFeEJpbiIsICJVQVNGbHVjdE9sZFNITTA1RXhCaW4iLCAiVUFTRmx1Y3RFQ01BMTBFeEJpbiIsICJVQVNGbHVjdEVDTUEwNUV4QmluIiwgIlVBU0ZsdWN0RloxMEV4TWF4TFIiLCAiVUFTRmx1Y3RGWjA1RXhNYXhMUiIsICJVQVNGbHVjdE9WMTBFeE1heExSIiwgIlVBU0ZsdWN0T1YwNUV4TWF4TFIiKQ0KZFZhciA8LSAiZEFubm95TWVhbiINCg0Kc2VlZHMgPC0gYygyNTEwNywgNTQ2MDk4LCAxOTUsIDU5MzcsIDEwMjY1OCkNCg0KYGBgDQoNCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSA1NTAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjUpDQoNCmBgYA0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRGbHVjdCA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRGbHVjdCRPT0JfUk1TRQ0KcmVzdWx0c091dEZsdWN0JE9PQl9NQUUNCnJlc3VsdHNPdXRGbHVjdCRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRGbHVjdCA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0Rmx1Y3QkT09CX1JNU0UNCnJlc3VsdHNPdXRGbHVjdCRPT0JfTUFFDQpyZXN1bHRzT3V0Rmx1Y3QkUnNxdWFyZWQNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWJzIGZsdWN0JywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0Rmx1Y3QkT09CX1JNU0UNCnJlc2RBbm5veU1uRml0QUJbJ0FicyBmbHVjdCcsICdNQUUnXSA8LSByZXN1bHRzT3V0Rmx1Y3QkT09CX01BRQ0KcmVzZEFubm95TW5GaXRBQlsnQWJzIGZsdWN0JywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dEZsdWN0JFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNGbHVjdCA8LSByZXN1bHRzT3V0Rmx1Y3QkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0zLjV9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0Rmx1Y3QuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dEZsdWN0JGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dEZsdWN0LmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEZsdWN0LmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0Rmx1Y3QuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s0XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyBnZ3RpdGxlKCJGbHVjdHVhdGlvbiBzdHJlbmd0aCIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkZsdWN0Q29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9My41LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkZsdWN0Q29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uRmx1Y3RDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0zLjUsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uRmx1Y3RDb25QZXJtaW1wLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCmZsdWN0VmFyIDwtICJVQVNGbHVjdEVDTUExMEV4QmluIg0KDQpgYGANCg0KIyMjIyMgUm91Z2huZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQojIFJvdWdobmVzcw0KaVZhcnMgPC0gYyhhYnNWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJVQVNSb3VnaEVDTUExMEV4QmluIiwgIlVBU1JvdWdoRUNNQTA1RXhCaW4iLCAiVUFTUm91Z2hGWjEwRXhNYXhMUiIsICJVQVNSb3VnaEZaMDVFeE1heExSIiwgIlVBU1JvdWdoRFcxMEV4TWF4TFIiLCAiVUFTUm91Z2hEVzA1RXhNYXhMUiIpDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDQ3MDEsIDUyMTg3LCAxNjU4OSwgNjUyMTcsIDE2ODkzKQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAxMDAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjgpDQoNCmBgYA0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRSb3VnaCA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRSb3VnaCRPT0JfUk1TRQ0KcmVzdWx0c091dFJvdWdoJE9PQl9NQUUNCnJlc3VsdHNPdXRSb3VnaCRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRSb3VnaCA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0Um91Z2gkT09CX1JNU0UNCnJlc3VsdHNPdXRSb3VnaCRPT0JfTUFFDQpyZXN1bHRzT3V0Um91Z2gkUnNxdWFyZWQNCg0KYGBgDQoNCmBgYHtyfQ0KIyBzdG9yZSByZXN1bHRzDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgcm91Z2gnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRSb3VnaCRPT0JfUk1TRQ0KcmVzZEFubm95TW5GaXRBQlsnQWJzIHJvdWdoJywgJ01BRSddIDwtIHJlc3VsdHNPdXRSb3VnaCRPT0JfTUFFDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgcm91Z2gnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0Um91Z2gkUnNxdWFyZWQNCnJlc2RBbm5veU1uUGVybUltcEFCJEFic1JvdWdoIDwtIHJlc3VsdHNPdXRSb3VnaCRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTIuOX0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRSb3VnaC5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0Um91Z2gkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0Um91Z2guY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0Um91Z2guY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRSb3VnaC5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzVdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIGdndGl0bGUoIlJvdWdobmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNblJvdWdoQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9Mi45LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNblJvdWdoQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uUm91Z2hDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0yLjksIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uUm91Z2hDb25QZXJtaW1wLnBkZiIpDQp9DQoNCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0Kcm91Z2hWYXIgPC0gIlVBU1JvdWdoRlowNUV4TWF4TFIiDQoNCmBgYA0KDQojIyMjIyBJbXB1bHNpdmVuZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KIyBJbXB1bHNpdmVuZXNzDQppVmFycyA8LSBjKGFic1ZhciwgZXZlbnRWYXIsIGFtYlZhciwgIlVBU0ltcHVsc1NITUF2Z01heExSIiwgIlVBU0ltcHVsc1NITTA1RXhNYXhMUiIsICJVQVNJbXB1bHNTSE1Qb3dBdmdNYXhMUiIsICJVQVNJbXB1bHNMb3VkV1pBdmdNYXhMUiIsICJVQVNJbXB1bHNMb3VkV1owNUV4TWF4TFIiLCAiVUFTSW1wdWxzTG91ZFdaUG93QXZnTWF4TFIiLCAiVUFTSW1wdWxzTG91ZFdFQ01BQXZnQmluIiwgIlVBU0ltcHVsc0xvdWRXRUNNQTA1RXhCaW4iLCAiVUFTSW1wdWxzTG91ZFdFQ01BUG93QXZnQmluIikNCmRWYXIgPC0gImRBbm5veU1lYW4iDQoNCnNlZWRzIDwtIGMoODQ5NSwgNTk4NjcsIDU0MTYsIDk4NDMsIDg2KQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSA0MDAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjI1KQ0KDQpgYGANCg0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRJbXB1bHMgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0SW1wdWxzJE9PQl9STVNFDQpyZXN1bHRzT3V0SW1wdWxzJE9PQl9NQUUNCnJlc3VsdHNPdXRJbXB1bHMkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0SW1wdWxzIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRJbXB1bHMkT09CX1JNU0UNCnJlc3VsdHNPdXRJbXB1bHMkT09CX01BRQ0KcmVzdWx0c091dEltcHVscyRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWJzIGltcHVscycsICdSTVNFJ10gPC0gcmVzdWx0c091dEltcHVscyRPT0JfUk1TRQ0KcmVzZEFubm95TW5GaXRBQlsnQWJzIGltcHVscycsICdNQUUnXSA8LSByZXN1bHRzT3V0SW1wdWxzJE9PQl9NQUUNCnJlc2RBbm5veU1uRml0QUJbJ0FicyBpbXB1bHMnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0SW1wdWxzJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNJbXB1bHMgPC0gcmVzdWx0c091dEltcHVscyRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTMuOH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRJbXB1bHMuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dEltcHVscyRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRJbXB1bHMuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbNl0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAobWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlKSIpICsgZ2d0aXRsZSgiSW1wdWxzaXZlbmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkltcHVsc0NvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTMuOCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5JbXB1bHNDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5JbXB1bHNDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0zLjgsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uSW1wdWxzQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQppbXB1bHNWYXIgPC0gIlVBU0ltcHVsc0xvdWRXWkF2Z01heExSIg0KDQpgYGANCg0KIyMjIyBTUU0gYW5kIGxvdWRuZXNzIGNvbXBhcmlzb24NCg0KTm93IHRoZSBoaWdoZXN0IGltcG9ydGFuY2UgU1FNcyBhcmUgcmFua2VkIGFnYWluc3QgZWFjaCBvdGhlciwgY29udHJvbGxpbmcgZm9yIFVBUyBsb3VkbmVzcyBhbmQgYW1iaWVudCBMQWVxLg0KDQojIyMjIyBJbmNsdWRlIHRvbmFsIGxvdWRuZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFic1ZhciwgZXZlbnRWYXIsIGFtYlZhciwgc2hhcnBWYXIsIHRvbkxkVmFyLCBmbHVjdFZhciwgcm91Z2hWYXIsIGltcHVsc1ZhcikNCmRWYXIgPC0gImRBbm5veU1lYW4iDQoNCnNlZWRzIDwtIGMoNzA0OTgsIDQsIDE0OTg2LCA0NTMsIDg2NCkNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMjUwMQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS42KQ0KDQpgYGANCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0U1FNczEgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0U1FNczEkT09CX1JNU0UNCnJlc3VsdHNPdXRTUU1zMSRPT0JfTUFFDQpyZXN1bHRzT3V0U1FNczEkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0U1FNczEgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMxJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczEkT09CX01BRQ0KcmVzdWx0c091dFNRTXMxJFJzcXVhcmVkDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgU1FNcyBpbmMgdG9uYWwgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNRTXMxJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgU1FNcyBpbmMgdG9uYWwgbG91ZCcsICdNQUUnXSA8LSByZXN1bHRzT3V0U1FNczEkT09CX01BRQ0KcmVzZEFubm95TW5GaXRBQlsnQWJzIFNRTXMgaW5jIHRvbmFsIGxvdWQnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0U1FNczEkUnNxdWFyZWQNCnJlc2RBbm5veU1uUGVybUltcEFCJEFic1NRTXMxIDwtIHJlc3VsdHNPdXRTUU1zMSRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTIuNH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRTUU1zMS5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0U1FNczEkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0U1FNczEuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0U1FNczEuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRTUU1zMS5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzddLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgMS4zKSkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFic1NRTXNUb25MZENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BYnNTUU1zVG9uTGRDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BYnNTUU1zVG9uTGRDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0yLjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWJzU1FNc1RvbkxkQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KIyMjIyMgRXhjbHVkZSB0b25hbCBsb3VkbmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhYnNWYXIsIGV2ZW50VmFyLCBhbWJWYXIsIHNoYXJwVmFyLCB0b25hbFZhciwgZmx1Y3RWYXIsIHJvdWdoVmFyLCBpbXB1bHNWYXIpDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDU0NiwgNTcyMDMsIDI3MDgzNSwgNjA1OTIsIDgwOTQpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDE1MDENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzEuNikNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFNRTXMyIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczIkT09CX01BRQ0KcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNRTXMyIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTUU1zMiRPT0JfUk1TRQ0KcmVzdWx0c091dFNRTXMyJE9PQl9NQUUNCnJlc3VsdHNPdXRTUU1zMiRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWJzIFNRTXMgbm8gdG9uYWwgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgU1FNcyBubyB0b25hbCBsb3VkJywgJ01BRSddIDwtIHJlc3VsdHNPdXRTUU1zMiRPT0JfTUFFDQpyZXNkQW5ub3lNbkZpdEFCWydBYnMgU1FNcyBubyB0b25hbCBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNTUU1zMiA8LSByZXN1bHRzT3V0U1FNczIkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yLjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0U1FNczIuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFNRTXMyJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFNRTXMyLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFNRTXMyLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0U1FNczIuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s3XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoeWxpbT1jKDAsIDEuMykpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BYnNTUU1zTm9Ub25MZENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BYnNTUU1zTm9Ub25MZENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFic1NRTXNOb1RvbkxkQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9Mi40LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFic1NRTXNOb1RvbkxkQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KIyMjIyBQc3ljaG9hY291c3RpYyBhbm5veWFuY2UgbWV0cmljcw0KDQojIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFtYlZhciwgIlVBU1BzeWNoQW5ub3lXaWRtYW5uIiwgIlVBU1BzeWNoQW5ub3lNb3JlIiwgIlVBU1BzeWNoQW5ub3lEaSIsICJVQVNQc3ljaEFubm95VG9yaWphIiwgIlVBU1BzeWNoQW5ub3lXaWxsZW1zZW4iLCAiVUFTUHN5Y2hBbm5veUJvdWNoZXIiKQ0KZFZhciA8LSAiZEFubm95TWVhbiINCg0Kc2VlZHMgPC0gYyg4MjksIDksIDE5MCwgNDU2NCwgOTI0NzA3ODI0KQ0KDQoNCmBgYA0KDQojIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAxNTAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjc1KQ0KDQpgYGANCg0KIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRQQSA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRQQSRPT0JfUk1TRQ0KcmVzdWx0c091dFBBJE9PQl9NQUUNCnJlc3VsdHNPdXRQQSRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRQQSA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0UEEkT09CX1JNU0UNCnJlc3VsdHNPdXRQQSRPT0JfTUFFDQpyZXN1bHRzT3V0UEEkUnNxdWFyZWQNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RBbm5veU1uRml0QUJbJ1BzeWNob2Fjb3VzdGljIGFubm95YW5jZScsICdSTVNFJ10gPC0gcmVzdWx0c091dFBBJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydQc3ljaG9hY291c3RpYyBhbm5veWFuY2UnLCAnTUFFJ10gPC0gcmVzdWx0c091dFBBJE9PQl9NQUUNCnJlc2RBbm5veU1uRml0QUJbJ1BzeWNob2Fjb3VzdGljIGFubm95YW5jZScsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRQQSRSc3F1YXJlZA0KcmVzZEFubm95TW5QZXJtSW1wQUIkQWJzUEEgPC0gcmVzdWx0c091dFBBJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yLjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0UEEuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFBBJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFBBLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFBBLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0UEEuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1sxMF0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAobWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCAxLjgpKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWJzUEFDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD0yLjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWJzUEFDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BYnNQQUNvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BYnNQQUNvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNCiMjIyBBbGwgdmFyaWFibGVzIChhYnNvbHV0ZSBhbmQgZGlmZmVyZW5jZSkNCg0KIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBuYW1lcyhzdGltRGF0YU51bSlbd2hpY2gobmFtZXMoc3RpbURhdGFOdW0pID09ICdVQVNFdmVudHMnKTp3aGljaChuYW1lcyhzdGltRGF0YU51bSkgPT0gJ2RQc3ljaEFubm95Qm91Y2hlcicpXQ0KDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDE0NTY5LCA5ODY1MSwgNTQ2NTQ0OTgsIDQ1NDk0OCwgNDEzMjEpDQoNCmBgYA0KDQojIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFZhcnNIeXBlclR1bmUuc3ZnIiwgd2lkdGg9MTIsIGhlaWdodD00LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFsbFZhcnNIeXBlclR1bmUuc3ZnIikNCg0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxWYXJzSHlwZXJUdW5lLnBkZiIsIHdpZHRoPTEyLCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxWYXJzSHlwZXJUdW5lLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDI1MDENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzMuNSkNCg0KYGBgDQoNCiMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRBYnNEaWZmcyA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRBYnNEaWZmcyRPT0JfUk1TRQ0KcmVzdWx0c091dEFic0RpZmZzJE9PQl9NQUUNCnJlc3VsdHNPdXRBYnNEaWZmcyRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRBYnNEaWZmcyA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0QWJzRGlmZnMkT09CX1JNU0UNCnJlc3VsdHNPdXRBYnNEaWZmcyRPT0JfTUFFDQpyZXN1bHRzT3V0QWJzRGlmZnMkUnNxdWFyZWQNCmBgYA0KDQpgYGB7cn0NCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWxsIHZhcnMnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRBYnNEaWZmcyRPT0JfUk1TRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIHZhcnMnLCAnTUFFJ10gPC0gcmVzdWx0c091dEFic0RpZmZzJE9PQl9NQUUNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCB2YXJzJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dEFic0RpZmZzJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBbGxWYXJzIDwtIHJlc3VsdHNPdXRBYnNEaWZmcyRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0zMH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0QWJzRGlmZnMkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzldLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxWYXJzQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9MzAsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsVmFyc0NvblBlcm1pbXAuc3ZnIikNCg0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxWYXJzQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9MzAsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsVmFyc0NvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTE4fQ0KDQojIFBsb3Qgb25seSBwb3NpdGl2ZSB2YWx1ZXMNCg0KcmVzdWx0c091dEFic0RpZmZzLmNvbmltcFB0diA8LSByZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wIHw+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4oJ01ldHJpYycpIHw+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJfaWYoaXMubnVtZXJpYywgYWxsX3ZhcnMoLiA+IDApKSB8Pg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCdNZXRyaWMnKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wUHR2KSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wUHR2KSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXBQdHYpKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzldLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxWYXJzQ29uUGVybWltcFB0di5zdmciLCB3aWR0aD04LCBoZWlnaHQ9MTgsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsVmFyc0NvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFZhcnNDb25QZXJtaW1wUHR2LnBkZiIsIHdpZHRoPTgsIGhlaWdodD0xOCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxWYXJzQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9Nn0NCg0KIyBQbG90IG9ubHkgdmFsdWVzIHdpdGhpbiAxJSBvZiB0aGUgbWF4aW11bQ0KDQpyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wMXBjIDwtIHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXAgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbignTWV0cmljJykgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcl9pZihpcy5udW1lcmljLCBhbGxfdmFycyguID4gbWF4KHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXApLzEwMCkpIHw+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoJ01ldHJpYycpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXAxcGMpICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXAxcGMpLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dEFic0RpZmZzLmNvbmltcDFwYykpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbOV0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAobWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFZhcnNDb25QZXJtaW1wMXBjLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD02LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFsbFZhcnNDb25QZXJtaW1wMXBjLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxWYXJzQ29uUGVybWltcDFwYy5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NiwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxWYXJzQ29uUGVybWltcDFwYy5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQphbGxWYXIgPC0gIkRldGVjdDBwMWRCSW50TWF4TFIiDQoNCmBgYA0KDQojIyMgZFNRTSBhbmFseXNpcw0KDQojIyMjIEluZGl2aWR1YWwgU1FNcw0KDQojIyMjIyBkU2hhcnBuZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFsbFZhciwgZXZlbnRWYXIsIGFtYlZhciwgImRTaGFycEF1cklTTzNQb3dBdmdCaW4iLCAiZFNoYXJwQXVySVNPMzA1RXhCaW4iLCAiZFNoYXJwQXVyU0hNUG93QXZnQmluIiwgImRTaGFycEF1clNITTA1RXhCaW4iLCAiZFRvblNocEF1clNITVBvd0F2Z0JpbiIsICJkVG9uU2hwQXVyU0hNMDVFeEJpbiIsICJQYXJ0VG9uU2hwQXVyU0hNUG93QXZnQmluIiwNCiAgICAgICAgICAgIlBhcnRUb25TaHBBdXJTSE0wNUV4QmluIiwgIlVBU1NoYXJwQXVySVNPM1Bvd0F2Z0JpbiIsICJVQVNTaGFycEF1cklTTzMwNUV4QmluIiwgIlVBU1NoYXJwQXVyU0hNUG93QXZnQmluIiwgIlVBU1NoYXJwQXVyU0hNMDVFeEJpbiIsICJVQVNTaGFycEF1cklTTzFQb3dBdmdCaW4iLCAiVUFTU2hhcnBBdXJJU08xMDVFeEJpbiIsICJVQVNTaGFycHZCSVNPMVBvd0F2Z0JpbiIsICJVQVNTaGFycHZCSVNPMTA1RXhCaW4iLCAiVUFTU2hhcnBESU5Qb3dBdmdCaW4iLCAiVUFTU2hhcnBESU4wNUV4QmluIiwgIlVBU1NoYXJwQXVySVNPMU1lZEJpbiIsDQogICAgICAgICAiVUFTVG9uU2hwQXVyU0hNUG93QXZnQmluIiwgIlVBU1RvblNocEF1clNITTA1RXhCaW4iKQ0KZFZhciA8LSAiZEFubm95TWVhbiINCg0Kc2VlZHMgPC0gYyg4NDE5NCwgOTA1LCA2NDgxNSwgOTI4MDU0LCA2MjUwOTEpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtMjUxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjI1KQ0KDQpgYGANCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0U2hhcnAgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0U2hhcnAkT09CX1JNU0UNCnJlc3VsdHNPdXRTaGFycCRPT0JfTUFFDQpyZXN1bHRzT3V0U2hhcnAkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0U2hhcnAgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNoYXJwJE9PQl9STVNFDQpyZXN1bHRzT3V0U2hhcnAkT09CX01BRQ0KcmVzdWx0c091dFNoYXJwJFJzcXVhcmVkDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCBzaGFycCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNoYXJwJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgc2hhcnAnLCAnTUFFJ10gPC0gcmVzdWx0c091dFNoYXJwJE9PQl9NQUUNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCBzaGFycCcsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRTaGFycCRSc3F1YXJlZA0KcmVzZEFubm95TW5QZXJtSW1wQUIkQWxsU2hhcnAgPC0gcmVzdWx0c091dFNoYXJwJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9NS40fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFNoYXJwLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRTaGFycCRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRTaGFycC5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRTaGFycC5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dFNoYXJwLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbMl0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAobWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlKSIpICsgZ2d0aXRsZSgiQWxsIHNoYXJwbmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFNoYXJwQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9NS40LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFsbFNoYXJwQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWxsU2hhcnBDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD01LjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsU2hhcnBDb25QZXJtaW1wLnBkZiIpDQp9DQoNCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0KYWxsU2hhcnBWYXIgPC0gImRTaGFycEF1cklTTzNQb3dBdmdCaW4iDQoNCmBgYA0KDQojIyMjIyBkVG9uYWwgbG91ZG5lc3MgYW5kIGR0b25hbGl0eQ0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhbGxWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJkVG9uYWxFQ01BQXZnTWF4TFIiLCAiZFRvbmFsU0hNSW50MDVFeE1heExSIiwgImRUb25hbFNITUludEF2Z01heExSIiwgImRUb25hbEVDTUEwNUV4TWF4TFIiLCAiZFRvbmFsQXdTSE1BdmdNYXhMUiIsCSJkVG9uYWxBd1NITTA1RXhNYXhMUiIsCSJkVG9uYWxBd1NITUludEF2Z01heExSIiwgCSJkVG9uYWxBd1NITUludDA1RXhNYXhMUiIsICJkVG9uTGRFQ01BUG93QXZnQmluIiwgImRUb25MZEVDTUEwNUV4QmluIiwgImRUb25TaHBBdXJTSE1Qb3dBdmdCaW4iLA0KICAgICAgICAgICAiZFRvblNocEF1clNITTA1RXhCaW4iLCAiUGFydFRvbkxkU0hNUG93QXZnQmluIiwgIlVBU1RvbmFsRUNNQUF2Z01heExSIiwgIlVBU1RvbmFsU0hNSW50MDVFeE1heExSIiwgIlVBU1RvbmFsU0hNSW50QXZnTWF4TFIiLCAiVUFTVG9uYWxFQ01BMDVFeE1heExSIiwgIlVBU1RvbmFsQXdTSE1BdmdNYXhMUiIsCSJVQVNUb25hbEF3U0hNMDVFeE1heExSIiwJIlVBU1RvbmFsQXdTSE1JbnRBdmdNYXhMUiIsIAkiVUFTVG9uYWxBd1NITUludDA1RXhNYXhMUiIsICJVQVNUb25MZEVDTUFQb3dBdmdCaW4iLCAiVUFTVG9uTGRFQ01BMDVFeEJpbiIsICJVQVNUb25hbEF1ckF2Z01heExSIiwgIlVBU1RvbmFsQXVyMDVFeE1heExSIiwgIlVBU1RvbmFsQXVyMTBFeE1heExSIiwNCiAgICAgICAgICJVQVNUb25TaHBBdXJTSE1Qb3dBdmdCaW4iLCAiVUFTVG9uU2hwQXVyU0hNMDVFeEJpbiIpDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDU2MTY4NCwgMTA0Nzk4LCAxNTM2LCA0OCwgNDg1NjEpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDI1MQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS4yNSkNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQojIFRvbmFsaXR5IHdpdGggdG9uYWwgbG91ZG5lc3MNCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0VG9uYWwxIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFRvbmFsMSRPT0JfUk1TRQ0KcmVzdWx0c091dFRvbmFsMSRPT0JfTUFFDQpyZXN1bHRzT3V0VG9uYWwxJFJzcXVhcmVkDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQojIFRvbmFsaXR5IHdpdGggdG9uYWwgbG91ZG5lc3MNCg0KcmVzdWx0c091dFRvbmFsMSA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0VG9uYWwxJE9PQl9STVNFDQpyZXN1bHRzT3V0VG9uYWwxJE9PQl9NQUUNCnJlc3VsdHNPdXRUb25hbDEkUnNxdWFyZWQNCg0KYGBgDQoNCmBgYHtyfQ0KIyBzdG9yZSByZXN1bHRzDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgdG9uYWwgaW5jIGxvdWQnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRUb25hbDEkT09CX1JNU0UNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCB0b25hbCBpbmMgbG91ZCcsICdNQUUnXSA8LSByZXN1bHRzT3V0VG9uYWwxJE9PQl9NQUUNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCB0b25hbCBpbmMgbG91ZCcsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRUb25hbDEkUnNxdWFyZWQNCnJlc2RBbm5veU1uUGVybUltcEFCJEFsbFRvbmFsMSA8LSByZXN1bHRzT3V0VG9uYWwxJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9Nn0NCg0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFRvbmFsMS5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0VG9uYWwxJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFRvbmFsMS5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRUb25hbDEuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRUb25hbDEuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1szXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyBnZ3RpdGxlKCJBbGwgdG9uYWxpdHkgaW5jLiB0b25hbCBsb3VkbmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCAyLjIpKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWxsVG9uYWxMZENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTYsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsVG9uYWxMZENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFRvbmFsTGRDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD02LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFsbFRvbmFsTGRDb25QZXJtaW1wLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCmFsbFRvbkxkVmFyIDwtICJkVG9uTGRFQ01BUG93QXZnQmluIg0KDQpgYGANCg0KIyMjIyMgZFRvbmFsaXR5IHdpdGhvdXQgZHRvbmFsIGxvdWRuZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFsbFZhciwgZXZlbnRWYXIsIGFtYlZhciwgImRUb25hbEVDTUFBdmdNYXhMUiIsICJkVG9uYWxTSE1JbnQwNUV4TWF4TFIiLCAiZFRvbmFsU0hNSW50QXZnTWF4TFIiLCAiZFRvbmFsRUNNQTA1RXhNYXhMUiIsICJkVG9uYWxBd1NITUF2Z01heExSIiwJImRUb25hbEF3U0hNMDVFeE1heExSIiwJImRUb25hbEF3U0hNSW50QXZnTWF4TFIiLCAJImRUb25hbEF3U0hNSW50MDVFeE1heExSIiwgIlVBU1RvbmFsRUNNQUF2Z01heExSIiwgIlVBU1RvbmFsU0hNSW50MDVFeE1heExSIiwgIlVBU1RvbmFsU0hNSW50QXZnTWF4TFIiLCAiVUFTVG9uYWxFQ01BMDVFeE1heExSIiwgIlVBU1RvbmFsQXdTSE1BdmdNYXhMUiIsCSJVQVNUb25hbEF3U0hNMDVFeE1heExSIiwJIlVBU1RvbmFsQXdTSE1JbnRBdmdNYXhMUiIsICJVQVNUb25hbEF3U0hNSW50MDVFeE1heExSIiwJIlVBU1RvbmFsQXVyQXZnTWF4TFIiLCAiVUFTVG9uYWxBdXIwNUV4TWF4TFIiLCAiVUFTVG9uYWxBdXIxMEV4TWF4TFIiKQ0KZFZhciA8LSAiZEFubm95TWVhbiINCg0Kc2VlZHMgPC0gYyg0MTA4NjUsIDI5NTQsIDcwODEyLCAyMDMsIDc5ODQpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDUwMQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS4yNSkNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQojIFRvbmFsaXR5DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFRvbmFsMiA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0VG9uYWwyJE9PQl9STVNFDQpyZXN1bHRzT3V0VG9uYWwyJE9PQl9NQUUNCnJlc3VsdHNPdXRUb25hbDIkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eQ0KDQpyZXN1bHRzT3V0VG9uYWwyIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRUb25hbDIkT09CX1JNU0UNCnJlc3VsdHNPdXRUb25hbDIkT09CX01BRQ0KcmVzdWx0c091dFRvbmFsMiRSc3F1YXJlZA0KDQpgYGANCg0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgdG9uYWwgbm8gbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFRvbmFsMiRPT0JfUk1TRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIHRvbmFsIG5vIGxvdWQnLCAnTUFFJ10gPC0gcmVzdWx0c091dFRvbmFsMiRPT0JfTUFFDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgdG9uYWwgbm8gbG91ZCcsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRUb25hbDIkUnNxdWFyZWQNCnJlc2RBbm5veU1uUGVybUltcEFCJEFsbFRvbmFsMiA8LSByZXN1bHRzT3V0VG9uYWwyJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9NS44fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFRvbmFsMi5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0VG9uYWwyJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFRvbmFsMi5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRUb25hbDIuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRUb25hbDIuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1szXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyBnZ3RpdGxlKCJBbGwgdG9uYWxpdHkgdy9vIHRvbmFsIGxvdWRuZXNzIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoeWxpbT1jKDAsIDIuNikpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxUb25hbENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTUuOCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxUb25hbENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJBbGxBbm5veU1uZFRvbmFsQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NS44LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFsbFRvbmFsQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCmFsbFRvbmFsVmFyIDwtICJkVG9uYWxTSE1JbnRBdmdNYXhMUiINCg0KYGBgDQoNCiMjIyMjIGRGbHVjdHVhdGlvbiBzdHJlbmd0aA0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KIyBGbHVjdHVhdGlvbiBzdHJlbmd0aA0KaVZhcnMgPC0gYyhhbGxWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJkRmx1Y3RFQ01BMTBFeEJpbiIsICJkRmx1Y3RFQ01BMDVFeEJpbiIsICJkRmx1Y3RPVjEwRXhNYXhMUiIsICJkRmx1Y3RPVjA1RXhNYXhMUiIsICJVQVNGbHVjdE9sZFNITTEwRXhCaW4iLCAiVUFTRmx1Y3RPbGRTSE0wNUV4QmluIiwgIlVBU0ZsdWN0RUNNQTEwRXhCaW4iLCAiVUFTRmx1Y3RFQ01BMDVFeEJpbiIsICJVQVNGbHVjdEZaMTBFeE1heExSIiwgIlVBU0ZsdWN0RlowNUV4TWF4TFIiLCAiVUFTRmx1Y3RPVjEwRXhNYXhMUiIsICJVQVNGbHVjdE9WMDVFeE1heExSIikNCmRWYXIgPC0gImRBbm5veU1lYW4iDQoNCnNlZWRzIDwtIGMoNDE4NjU3LCA4NCwgMTYzMCwgMTg2NTksIDM2ODcpDQoNCmBgYA0KDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gNDAwMQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS41KQ0KDQpgYGANCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0Rmx1Y3QgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0Rmx1Y3QkT09CX1JNU0UNCnJlc3VsdHNPdXRGbHVjdCRPT0JfTUFFDQpyZXN1bHRzT3V0Rmx1Y3QkUnNxdWFyZWQNCg0KYGBgDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dEZsdWN0IDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRGbHVjdCRPT0JfUk1TRQ0KcmVzdWx0c091dEZsdWN0JE9PQl9NQUUNCnJlc3VsdHNPdXRGbHVjdCRSc3F1YXJlZA0KDQpgYGANCg0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgZmx1Y3QnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRGbHVjdCRPT0JfUk1TRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIGZsdWN0JywgJ01BRSddIDwtIHJlc3VsdHNPdXRGbHVjdCRPT0JfTUFFDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgZmx1Y3QnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0Rmx1Y3QkUnNxdWFyZWQNCnJlc2RBbm5veU1uUGVybUltcEFCJEFsbEZsdWN0IDwtIHJlc3VsdHNPdXRGbHVjdCRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0Rmx1Y3QuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dEZsdWN0JGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dEZsdWN0LmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEZsdWN0LmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0Rmx1Y3QuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s0XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyBnZ3RpdGxlKCJBbGwgZmx1Y3R1YXRpb24gc3RyZW5ndGgiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxGbHVjdENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsRmx1Y3RDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxGbHVjdENvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsRmx1Y3RDb25QZXJtaW1wLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCmFsbEZsdWN0VmFyIDwtICJkRmx1Y3RFQ01BMTBFeEJpbiINCg0KYGBgDQoNCg0KIyMjIyMgZFJvdWdobmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KIyBSb3VnaG5lc3MNCmlWYXJzIDwtIGMoYWxsVmFyLCBldmVudFZhciwgYW1iVmFyLCAiZFJvdWdoRUNNQTEwRXhCaW4iLCAiZFJvdWdoRUNNQTA1RXhCaW4iLCAiZFJvdWdoRloxMEV4TWF4TFIiLCAiZFJvdWdoRlowNUV4TWF4TFIiLCAiVUFTUm91Z2hFQ01BMTBFeEJpbiIsICJVQVNSb3VnaEVDTUEwNUV4QmluIiwgIlVBU1JvdWdoRloxMEV4TWF4TFIiLCAiVUFTUm91Z2hGWjA1RXhNYXhMUiIsICJVQVNSb3VnaERXMTBFeE1heExSIiwgIlVBU1JvdWdoRFcwNUV4TWF4TFIiKQ0KZFZhciA8LSAiZEFubm95TWVhbiINCg0Kc2VlZHMgPC0gYyg2OTg1MSwgODUxMDksIDQxMDk4NiwgMTU2MywgODk2KQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAxMDAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjI1KQ0KDQpgYGANCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0Um91Z2ggPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0Um91Z2gkT09CX1JNU0UNCnJlc3VsdHNPdXRSb3VnaCRPT0JfTUFFDQpyZXN1bHRzT3V0Um91Z2gkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0Um91Z2ggPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFJvdWdoJE9PQl9STVNFDQpyZXN1bHRzT3V0Um91Z2gkT09CX01BRQ0KcmVzdWx0c091dFJvdWdoJFJzcXVhcmVkDQoNCmBgYA0KDQpgYGB7cn0NCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWxsIHJvdWdoJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0Um91Z2gkT09CX1JNU0UNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCByb3VnaCcsICdNQUUnXSA8LSByZXN1bHRzT3V0Um91Z2gkT09CX01BRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIHJvdWdoJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFJvdWdoJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBbGxSb3VnaCA8LSByZXN1bHRzT3V0Um91Z2gkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD00fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFJvdWdoLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRSb3VnaCRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRSb3VnaC5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRSb3VnaC5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dFJvdWdoLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbNV0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAobWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlKSIpICsgZ2d0aXRsZSgiQWxsIHJvdWdobmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFJvdWdoQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxSb3VnaENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFJvdWdoQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxSb3VnaENvblBlcm1pbXAucGRmIikNCn0NCg0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQphbGxSb3VnaFZhciA8LSAiZFJvdWdoRlowNUV4TWF4TFIiDQoNCmBgYA0KDQojIyMjIyBkSW1wdWxzaXZlbmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCiMgSW1wdWxzaXZlbmVzcw0KaVZhcnMgPC0gYyhhbGxWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJkSW1wdWxzU0hNQXZnTWF4TFIiLCAiZEltcHVsc1NITTA1RXhNYXhMUiIsICJkSW1wdWxzU0hNUG93QXZnTWF4TFIiLA0KICAgICAgICAgICAiZEltcHVsc0xvdWRXWkF2Z01heExSIiwgImRJbXB1bHNMb3VkV1owNUV4TWF4TFIiLCAiZEltcHVsc0xvdWRXWlBvd0F2Z01heExSIiwNCiAgICAgICAgICAgImRJbXB1bHNMb3VkV0VDTUFBdmdCaW4iLCAiZEltcHVsc0xvdWRXRUNNQTA1RXhCaW4iLCAiZEltcHVsc0xvdWRXRUNNQVBvd0F2Z0JpbiIsICJVQVNJbXB1bHNTSE1BdmdNYXhMUiIsICJVQVNJbXB1bHNTSE0wNUV4TWF4TFIiLCAiVUFTSW1wdWxzU0hNUG93QXZnTWF4TFIiLCAiVUFTSW1wdWxzTG91ZFdaQXZnTWF4TFIiLCAiVUFTSW1wdWxzTG91ZFdaMDVFeE1heExSIiwgIlVBU0ltcHVsc0xvdWRXWlBvd0F2Z01heExSIiwgIlVBU0ltcHVsc0xvdWRXRUNNQUF2Z0JpbiIsICJVQVNJbXB1bHNMb3VkV0VDTUEwNUV4QmluIiwgIlVBU0ltcHVsc0xvdWRXRUNNQVBvd0F2Z0JpbiIpDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDQxODY1OSwgNzgwNSwgMzg0NzUsIDY1ODM0LCAxNjUzKQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSA1MDENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzEuMjUpDQoNCmBgYA0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRJbXB1bHMgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0SW1wdWxzJE9PQl9STVNFDQpyZXN1bHRzT3V0SW1wdWxzJE9PQl9NQUUNCnJlc3VsdHNPdXRJbXB1bHMkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0SW1wdWxzIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRJbXB1bHMkT09CX1JNU0UNCnJlc3VsdHNPdXRJbXB1bHMkT09CX01BRQ0KcmVzdWx0c091dEltcHVscyRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWxsIGltcHVscycsICdSTVNFJ10gPC0gcmVzdWx0c091dEltcHVscyRPT0JfUk1TRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIGltcHVscycsICdNQUUnXSA8LSByZXN1bHRzT3V0SW1wdWxzJE9PQl9NQUUNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCBpbXB1bHMnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0SW1wdWxzJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBbGxJbXB1bHMgPC0gcmVzdWx0c091dEltcHVscyRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTUuNn0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRJbXB1bHMuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dEltcHVscyRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRJbXB1bHMuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbNl0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAobWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlKSIpICsgZ2d0aXRsZSgiQWxsIGltcHVsc2l2ZW5lc3MiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxJbXB1bHNDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD01LjYsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsSW1wdWxzQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWxsSW1wdWxzQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NS42LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFsbEltcHVsc0NvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0KYWxsSW1wdWxzVmFyIDwtICJkSW1wdWxzTG91ZFdaQXZnTWF4TFIiDQoNCmBgYA0KDQojIyMjIGRTUU0gYW5kIGxvdWRuZXNzIGNvbXBhcmlzb24NCg0KTm93IHRoZSBoaWdoZXN0IGltcG9ydGFuY2UgZFNRTXMgYXJlIHJhbmtlZCBhZ2FpbnN0IGVhY2ggb3RoZXIsIGNvbnRyb2xsaW5nIGZvciBsb3VkbmVzcyBkaWZmZXJlbmNlLg0KDQojIyMjIyBJbmNsdWRlIGR0b25hbCBsb3VkbmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhbGxWYXIsIGV2ZW50VmFyLCBhbWJWYXIsIGFsbFNoYXJwVmFyLCBhbGxUb25MZFZhciwgYWxsRmx1Y3RWYXIsIGFsbFJvdWdoVmFyLCBhbGxJbXB1bHNWYXIpDQpkVmFyIDwtICJkQW5ub3lNZWFuIg0KDQpzZWVkcyA8LSBjKDk4NDY1LCA1NDE2MywgNjU0MSwgMzY0ODUsIDg0OTY3NSkNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gNTAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8yKQ0KDQpgYGANCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0U1FNczEgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0U1FNczEkT09CX1JNU0UNCnJlc3VsdHNPdXRTUU1zMSRPT0JfTUFFDQpyZXN1bHRzT3V0U1FNczEkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0U1FNczEgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMxJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczEkT09CX01BRQ0KcmVzdWx0c091dFNRTXMxJFJzcXVhcmVkDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgU1FNcyBpbmMgdG9uYWwgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNRTXMxJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgU1FNcyBpbmMgdG9uYWwgbG91ZCcsICdNQUUnXSA8LSByZXN1bHRzT3V0U1FNczEkT09CX01BRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIFNRTXMgaW5jIHRvbmFsIGxvdWQnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0U1FNczEkUnNxdWFyZWQNCnJlc2RBbm5veU1uUGVybUltcEFCJEFsbFNRTXMxIDwtIHJlc3VsdHNPdXRTUU1zMSRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTIuNH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRTUU1zMS5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0U1FNczEkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0U1FNczEuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0U1FNczEuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRTUU1zMS5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzddLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgMikpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxTUU1zVG9uTGRDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD0yLjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsU1FNc1RvbkxkQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWxsU1FNc1RvbkxkQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9Mi40LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkQW5ub3lNbkFsbFNRTXNUb25MZENvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNCiMjIyMjIEV4Y2x1ZGUgdG9uYWwgbG91ZG5lc3MNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYWxsVmFyLCBldmVudFZhciwgYW1iVmFyLCBhbGxTaGFycFZhciwgYWxsVG9uYWxWYXIsIGFsbEZsdWN0VmFyLCBhbGxSb3VnaFZhciwgYWxsSW1wdWxzVmFyKQ0KZFZhciA8LSAiZEFubm95TWVhbiINCg0Kc2VlZHMgPC0gYyg0OTg2NSwgNzg1MiwgODQ1OTYxLCA0MTA1ODMsIDM2NzQ4KQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAyNTAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjYpDQoNCmBgYA0KDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFNRTXMyIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczIkT09CX01BRQ0KcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNRTXMyIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTUU1zMiRPT0JfUk1TRQ0KcmVzdWx0c091dFNRTXMyJE9PQl9NQUUNCnJlc3VsdHNPdXRTUU1zMiRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEFubm95TW5GaXRBQlsnQWxsIFNRTXMgbm8gdG9uYWwgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgU1FNcyBubyB0b25hbCBsb3VkJywgJ01BRSddIDwtIHJlc3VsdHNPdXRTUU1zMiRPT0JfTUFFDQpyZXNkQW5ub3lNbkZpdEFCWydBbGwgU1FNcyBubyB0b25hbCBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQpyZXNkQW5ub3lNblBlcm1JbXBBQiRBbGxTUU1zMiA8LSByZXN1bHRzT3V0U1FNczIkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yLjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0U1FNczIuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFNRTXMyJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFNRTXMyLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFNRTXMyLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0U1FNczIuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s3XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIChtZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UpIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoeWxpbT1jKDAsIDIpKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRBbm5veU1uQWxsU1FNc05vVG9uTGRDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD0yLjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRBbm5veU1uQWxsU1FNc05vVG9uTGRDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEFubm95TW5BbGxTUU1zTm9Ub25MZENvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxTUU1zTm9Ub25MZENvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNCiMjIyMgZFBzeWNob2Fjb3VzdGljIGFubm95YW5jZSBtZXRyaWNzDQoNCiMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYW1iVmFyLCAiZFBzeWNoQW5ub3lXaWRtYW5uIiwgImRQc3ljaEFubm95TW9yZSIsICJkUHN5Y2hBbm5veURpIiwgImRQc3ljaEFubm95VG9yaWphIiwgImRQc3ljaEFubm95V2lsbGVtc2VuIiwgImRQc3ljaEFubm95Qm91Y2hlciIsICJVQVNQc3ljaEFubm95V2lkbWFubiIsICJVQVNQc3ljaEFubm95TW9yZSIsICJVQVNQc3ljaEFubm95RGkiLCAiVUFTUHN5Y2hBbm5veVRvcmlqYSIsICJVQVNQc3ljaEFubm95V2lsbGVtc2VuIiwgIlVBU1BzeWNoQW5ub3lCb3VjaGVyIikNCmRWYXIgPC0gImRBbm5veU1lYW4iDQoNCnNlZWRzIDwtIGMoNDc4OTY2NDMsIDQ3NSwgNjU0LCA5ODk4NzEzMiwgNTQ0NikNCg0KDQpgYGANCg0KIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDQwMDENCm10cnkgPC0gIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjI1KQ0KDQpgYGANCg0KIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRQQSA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRQQSRPT0JfUk1TRQ0KcmVzdWx0c091dFBBJE9PQl9NQUUNCnJlc3VsdHNPdXRQQSRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRQQSA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0UEEkT09CX1JNU0UNCnJlc3VsdHNPdXRQQSRPT0JfTUFFDQpyZXN1bHRzT3V0UEEkUnNxdWFyZWQNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RBbm5veU1uRml0QUJbJ0FsbCBQc3ljaG9hY291c3RpYyBhbm5veWFuY2UnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRQQSRPT0JfUk1TRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIFBzeWNob2Fjb3VzdGljIGFubm95YW5jZScsICdNQUUnXSA8LSByZXN1bHRzT3V0UEEkT09CX01BRQ0KcmVzZEFubm95TW5GaXRBQlsnQWxsIFBzeWNob2Fjb3VzdGljIGFubm95YW5jZScsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRQQSRSc3F1YXJlZA0KcmVzZEFubm95TW5QZXJtSW1wQUIkQWxsUEEgPC0gcmVzdWx0c091dFBBJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD00fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFBBLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRQQSRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRQQS5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRQQS5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dFBBLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbMTBdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKG1lYW4gY2hhbmdlIGluIGFubm95YW5jZSkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgMS44KSkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFBBQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxQQUNvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkQW5ub3lNbkFsbFBBQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEFubm95TW5BbGxQQUNvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNCiMjIyBTYXZlIHRoZSByZXN1bHRzIG91dHB1dHMgdG8gZmlsZQ0KDQpgYGB7cn0NCg0KaWYgKHNhdmVkYXRhKXsNCiAgdXRpbHM6OndyaXRlLmNzdihyZXNkQW5ub3lNbkZpdEFCLCBwYXN0ZShvdXREYXRhUGF0aCwgIlxcUHRzQUJDUkZkQW5ub3lNbk9PQkZpdC5jc3YiLCBzZXA9IiIpKQ0KICBpaSA8LSAwDQogIHRlbXAgPSBsaXN0KCkNCiAgZm9yIChyZXMgaW4gcmVzZEFubm95TW5QZXJtSW1wQUIpew0KICAgIGlpIDwtIGlpICsgMQ0KICAgIHRlbXBbW2lpXV0gPC0gYXMuZGF0YS5mcmFtZShyZXNkQW5ub3lNblBlcm1JbXBBQltpaV0pDQogICAgbmFtZXModGVtcFtbaWldXSkgPC0gbmFtZXMocmVzZEFubm95TW5QZXJtSW1wQUJbaWldKQ0KICB9DQogIG9wZW54bHN4Ojp3cml0ZS54bHN4KHRlbXAsIHBhc3RlKG91dERhdGFQYXRoLCAiXFxQdHNBQkNSRmRBbm5veU1uQ29uUGVybWltcC54bHN4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgcm93TmFtZXM9VFJVRSkNCn0NCg0KYGBgDQoNCiMjIChDaGFuZ2UgdG8pIEhpZ2ggYW5ub3lhbmNlDQoNCiMjIyBJbml0aWFsaXNlIHJlc3VsdHMgb3V0cHV0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCnJlc2RIaUFubm95Rml0QUIgPC0gZGF0YS5mcmFtZShSTVNFID0gbnVtZXJpYygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNQUUgPSBudW1lcmljKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJzcXVhcmVkID0gbnVtZXJpYygpKQ0KcmVzZEhpQW5ub3lQZXJtSW1wQUIgPC0gbGlzdCgpDQoNCmBgYA0KDQojIyMgQWJzb2x1dGUgdmFyaWFibGVzDQoNCiMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gbmFtZXMoc3RpbURhdGFOdW0pW3doaWNoKG5hbWVzKHN0aW1EYXRhTnVtKSA9PSAnVUFTRXZlbnRzJyk6d2hpY2gobmFtZXMoc3RpbURhdGFOdW0pID09ICdVQVNQc3ljaEFubm95Qm91Y2hlcicpXQ0KaVZhcnMgPC0gaVZhcnNbISBpVmFycyAlaW4lIGMoJ1NOUmxldmVsJywgJ0ludGVybWl0UmF0aW9DMk1heExSJywgJ0ludGVybWl0UmF0aW9DM01heExSJywgJ0ludGVybWl0UmF0aW9DNU1heExSJyldDQpkVmFyIDwtICJkSGlnaEFubm95UGMiDQoNCnNlZWRzIDwtIGMoNTc4MzEyLCA1NDQsIDg0ODk0LCA1NDY1NCwgMTUzMTU3KQ0KDQpgYGANCg0KIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFic1ZhcnNIeXBlclR1bmUuc3ZnIiwgd2lkdGg9MTIsIGhlaWdodD00LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFic1ZhcnNIeXBlclR1bmUuc3ZnIikNCg0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBYnNWYXJzSHlwZXJUdW5lLnBkZiIsIHdpZHRoPTEyLCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBYnNWYXJzSHlwZXJUdW5lLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDE1MDENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzEuNzUpDQoNCmBgYA0KDQojIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0QWJzIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dEFicyRPT0JfUk1TRQ0KcmVzdWx0c091dEFicyRPT0JfTUFFDQpyZXN1bHRzT3V0QWJzJFJzcXVhcmVkDQoNCmBgYA0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRBYnMgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dEFicyRPT0JfUk1TRQ0KcmVzdWx0c091dEFicyRPT0JfTUFFDQpyZXN1bHRzT3V0QWJzJFJzcXVhcmVkDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FicyB2YXJzJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0QWJzJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgdmFycycsICdNQUUnXSA8LSByZXN1bHRzT3V0QWJzJE9PQl9NQUUNCnJlc2RIaUFubm95Rml0QUJbJ0FicyB2YXJzJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dEFicyRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWJzVmFycyA8LSByZXN1bHRzT3V0QWJzJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTE0fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dEFicy5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0QWJzJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dEFicy5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRBYnMuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRBYnMuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1sxXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKw0KICBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFic1ZhcnNDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD0xNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBYnNWYXJzQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95QWJzVmFyc0NvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTE0LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFic1ZhcnNDb25QZXJtaW1wLnBkZiIpDQp9DQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9MTB9DQoNCiMgUGxvdCBvbmx5IHBvc2l0aXZlIHZhbHVlcw0KcmVzdWx0c091dEFicy5jb25pbXBQdHYgPC0gcmVzdWx0c091dEFicy5jb25pbXAgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbignTWV0cmljJykgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcl9pZihpcy5udW1lcmljLCBhbGxfdmFycyguID4gMCkpIHw+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoJ01ldHJpYycpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRBYnMuY29uaW1wUHR2LCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEFicy5jb25pbXBQdHYpLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dEFicy5jb25pbXBQdHYpKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzFdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKCUgaGlnaGx5IGFubm95ZWQpIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95QWJzVmFyc0NvblBlcm1pbXBQdHYuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTEwLCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFic1ZhcnNDb25QZXJtaW1wUHR2LnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBYnNWYXJzQ29uUGVybWltcFB0di5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9MTAsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWJzVmFyc0NvblBlcm1pbXBQdHYucGRmIikNCn0NCg0KYGBgDQoNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9NH0NCg0KIyBQbG90IG9ubHkgdmFsdWVzIHdpdGhpbiAxJSBvZiB0aGUgbWF4aW11bQ0KcmVzdWx0c091dEFicy5jb25pbXAxcGMgPC0gcmVzdWx0c091dEFicy5jb25pbXAgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbignTWV0cmljJykgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcl9pZihpcy5udW1lcmljLCBhbGxfdmFycyguID4gbWF4KHJlc3VsdHNPdXRBYnMuY29uaW1wKS8xMDApKSB8Pg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCdNZXRyaWMnKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0QWJzLmNvbmltcDFwYywpICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRBYnMuY29uaW1wMXBjKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRBYnMuY29uaW1wMXBjKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1sxXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFic1ZhcnNDb25QZXJtaW1wMXBjLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD00LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFic1ZhcnNDb25QZXJtaW1wMXBjLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBYnNWYXJzQ29uUGVybWltcDFwYy5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBYnNWYXJzQ29uUGVybWltcDFwYy5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQphYnNWYXIgPC0gIlVBU0xvdWRFQ01BUG93QXZnQmluIg0KDQpgYGANCg0KIyMjIFNRTSBhbmFseXNpcw0KDQojIyMjIEluZGl2aWR1YWwgU1FNcw0KDQojIyMjIyBTaGFycG5lc3MNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYWJzVmFyLCBldmVudFZhciwgYW1iVmFyLCAiVUFTU2hhcnBBdXJJU08zUG93QXZnQmluIiwgIlVBU1NoYXJwQXVySVNPMzA1RXhCaW4iLCAiVUFTU2hhcnBBdXJTSE1Qb3dBdmdCaW4iLCAiVUFTU2hhcnBBdXJTSE0wNUV4QmluIiwgIlVBU1NoYXJwQXVySVNPMVBvd0F2Z0JpbiIsICJVQVNTaGFycEF1cklTTzEwNUV4QmluIiwgIlVBU1NoYXJwdkJJU08xUG93QXZnQmluIiwgIlVBU1NoYXJwdkJJU08xMDVFeEJpbiIsICJVQVNTaGFycERJTlBvd0F2Z0JpbiIsICJVQVNTaGFycERJTjA1RXhCaW4iLCAiVUFTU2hhcnBBdXJJU08xTWVkQmluIiwNCiAgICAgICAgICJVQVNUb25TaHBBdXJTSE1Qb3dBdmdCaW4iLCAiVUFTVG9uU2hwQXVyU0hNMDVFeEJpbiIpDQpkVmFyIDwtICJkSGlnaEFubm95UGMiDQoNCnNlZWRzIDwtIGMoNzA0MSwgOTA1LCA0OTg0NjUxLCA2NTEzMjEzLCAxMjA2NTEpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMTUwMQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMi4yNSkNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFNoYXJwIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNoYXJwJE9PQl9STVNFDQpyZXN1bHRzT3V0U2hhcnAkT09CX01BRQ0KcmVzdWx0c091dFNoYXJwJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNoYXJwIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTaGFycCRPT0JfUk1TRQ0KcmVzdWx0c091dFNoYXJwJE9PQl9NQUUNCnJlc3VsdHNPdXRTaGFycCRSc3F1YXJlZA0KDQpgYGANCg0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgc2hhcnAnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRTaGFycCRPT0JfUk1TRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIHNoYXJwJywgJ01BRSddIDwtIHJlc3VsdHNPdXRTaGFycCRPT0JfTUFFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgc2hhcnAnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0U2hhcnAkUnNxdWFyZWQNCnJlc2RIaUFubm95UGVybUltcEFCJEFic1NoYXJwIDwtIHJlc3VsdHNPdXRTaGFycCRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTQuOX0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRTaGFycC5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0U2hhcnAkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0U2hhcnAuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0U2hhcnAuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRTaGFycC5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzJdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKCUgaGlnaGx5IGFubm95ZWQpIikgKyBnZ3RpdGxlKCJTaGFycG5lc3MiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lTaGFycENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTQuOSwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lTaGFycENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veVNoYXJwQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NC45LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veVNoYXJwQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQpzaGFycFZhciA8LSAiVUFTU2hhcnBBdXJJU08zUG93QXZnQmluIg0KDQpgYGANCg0KDQojIyMjIyBUb25hbCBsb3VkbmVzcyBhbmQgdG9uYWxpdHkNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYWJzVmFyLCBldmVudFZhciwgYW1iVmFyLCAiVUFTVG9uYWxFQ01BQXZnTWF4TFIiLCAiVUFTVG9uYWxTSE1JbnQwNUV4TWF4TFIiLCAiVUFTVG9uYWxTSE1JbnRBdmdNYXhMUiIsICJVQVNUb25hbEVDTUEwNUV4TWF4TFIiLCAiVUFTVG9uYWxBd1NITUF2Z01heExSIiwJIlVBU1RvbmFsQXdTSE0wNUV4TWF4TFIiLAkiVUFTVG9uYWxBd1NITUludEF2Z01heExSIiwgCSJVQVNUb25hbEF3U0hNSW50MDVFeE1heExSIiwgIlVBU1RvbkxkRUNNQVBvd0F2Z0JpbiIsICJVQVNUb25MZEVDTUEwNUV4QmluIiwgIlVBU1RvbmFsQXVyQXZnTWF4TFIiLCAiVUFTVG9uYWxBdXIwNUV4TWF4TFIiLCAiVUFTVG9uYWxBdXIxMEV4TWF4TFIiLA0KICAgICAgICAgIlVBU1RvblNocEF1clNITVBvd0F2Z0JpbiIsICJVQVNUb25TaHBBdXJTSE0wNUV4QmluIikNCmRWYXIgPC0gImRIaWdoQW5ub3lQYyINCg0Kc2VlZHMgPC0gYyg1NDAsIDEwNDc5OCwgNDU2NDY0LCA4NzMzMSwgOTQ1NjQpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDI1MQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS41KQ0KDQpgYGANCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCiMgVG9uYWxpdHkgd2l0aCB0b25hbCBsb3VkbmVzcw0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRUb25hbDEgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0VG9uYWwxJE9PQl9STVNFDQpyZXN1bHRzT3V0VG9uYWwxJE9PQl9NQUUNCnJlc3VsdHNPdXRUb25hbDEkUnNxdWFyZWQNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCiMgVG9uYWxpdHkgd2l0aCB0b25hbCBsb3VkbmVzcw0KDQpyZXN1bHRzT3V0VG9uYWwxIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRUb25hbDEkT09CX1JNU0UNCnJlc3VsdHNPdXRUb25hbDEkT09CX01BRQ0KcmVzdWx0c091dFRvbmFsMSRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FicyB0b25hbCBpbmMgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFRvbmFsMSRPT0JfUk1TRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIHRvbmFsIGluYyBsb3VkJywgJ01BRSddIDwtIHJlc3VsdHNPdXRUb25hbDEkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIHRvbmFsIGluYyBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFRvbmFsMSRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWJzVG9uYWwxIDwtIHJlc3VsdHNPdXRUb25hbDEkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD00LjR9DQoNCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRUb25hbDEuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFRvbmFsMSRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRUb25hbDEuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0VG9uYWwxLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0VG9uYWwxLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbM10sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIGdndGl0bGUoIlRvbmFsaXR5IGluYy4gdG9uYWwgbG91ZG5lc3MiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgMTEwKSkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veVRvbmFsTGRDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD00LjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95VG9uYWxMZENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veVRvbmFsTGRDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD00LjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95VG9uYWxMZENvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0KdG9uTGRWYXIgPC0gIlVBU1RvbkxkRUNNQVBvd0F2Z0JpbiINCg0KYGBgDQoNCg0KIyMjIyMgVG9uYWxpdHkgd2l0aG91dCB0b25hbCBsb3VkbmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhYnNWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJVQVNUb25hbEVDTUFBdmdNYXhMUiIsICJVQVNUb25hbFNITUludDA1RXhNYXhMUiIsICJVQVNUb25hbFNITUludEF2Z01heExSIiwgIlVBU1RvbmFsRUNNQTA1RXhNYXhMUiIsICJVQVNUb25hbEF3U0hNQXZnTWF4TFIiLAkiVUFTVG9uYWxBd1NITTA1RXhNYXhMUiIsCSJVQVNUb25hbEF3U0hNSW50QXZnTWF4TFIiLCAiVUFTVG9uYWxBd1NITUludDA1RXhNYXhMUiIsCSJVQVNUb25hbEF1ckF2Z01heExSIiwgIlVBU1RvbmFsQXVyMDVFeE1heExSIiwgIlVBU1RvbmFsQXVyMTBFeE1heExSIikNCmRWYXIgPC0gImRIaWdoQW5ub3lQYyINCg0Kc2VlZHMgPC0gYygxNTYwODksIDU4NjAsIDEwNTI4LCA4OTU0MSwgNDY4NTE0NikNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMjUxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjI1KQ0KDQpgYGANCg0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRUb25hbDIgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRUb25hbDIkT09CX1JNU0UNCnJlc3VsdHNPdXRUb25hbDIkT09CX01BRQ0KcmVzdWx0c091dFRvbmFsMiRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQojIFRvbmFsaXR5DQoNCnJlc3VsdHNPdXRUb25hbDIgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFRvbmFsMiRPT0JfUk1TRQ0KcmVzdWx0c091dFRvbmFsMiRPT0JfTUFFDQpyZXN1bHRzT3V0VG9uYWwyJFJzcXVhcmVkDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FicyB0b25hbCBubyBsb3VkJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0VG9uYWwyJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgdG9uYWwgbm8gbG91ZCcsICdNQUUnXSA8LSByZXN1bHRzT3V0VG9uYWwyJE9PQl9NQUUNCnJlc2RIaUFubm95Rml0QUJbJ0FicyB0b25hbCBubyBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFRvbmFsMiRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWJzVG9uYWwyIDwtIHJlc3VsdHNPdXRUb25hbDIkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0zLjh9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0VG9uYWwyLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRUb25hbDIkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0VG9uYWwyLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFRvbmFsMi5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dFRvbmFsMi5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzNdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKCUgaGlnaGx5IGFubm95ZWQpIikgKyBnZ3RpdGxlKCJUb25hbGl0eSB3L28gdG9uYWwgbG91ZG5lc3MiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgMTEwKSkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veVRvbmFsQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9My44LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veVRvbmFsQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95VG9uYWxDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0zLjgsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95VG9uYWxDb25QZXJtaW1wLnBkZiIpDQp9DQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCnRvbmFsVmFyIDwtICJVQVNUb25hbEF3U0hNSW50MDVFeE1heExSIg0KDQpgYGANCg0KIyMjIyMgRmx1Y3R1YXRpb24gc3RyZW5ndGgNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCiMgRmx1Y3R1YXRpb24gc3RyZW5ndGgNCmlWYXJzIDwtIGMoYWJzVmFyLCBldmVudFZhciwgYW1iVmFyLCAiVUFTRmx1Y3RPbGRTSE0xMEV4QmluIiwgIlVBU0ZsdWN0T2xkU0hNMDVFeEJpbiIsICJVQVNGbHVjdEVDTUExMEV4QmluIiwgIlVBU0ZsdWN0RUNNQTA1RXhCaW4iLCAiVUFTRmx1Y3RGWjEwRXhNYXhMUiIsICJVQVNGbHVjdEZaMDVFeE1heExSIiwgIlVBU0ZsdWN0T1YxMEV4TWF4TFIiLCAiVUFTRmx1Y3RPVjA1RXhNYXhMUiIpDQpkVmFyIDwtICJkSGlnaEFubm95UGMiDQoNCnNlZWRzIDwtIGMoMjUxMDcsIDU0NjA5OCwgMTk1LCA1OTM3LCAxMDI2NTgpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDI1MQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS4yNSkNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dEZsdWN0IDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dEZsdWN0JE9PQl9STVNFDQpyZXN1bHRzT3V0Rmx1Y3QkT09CX01BRQ0KcmVzdWx0c091dEZsdWN0JFJzcXVhcmVkDQoNCmBgYA0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRGbHVjdCA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0Rmx1Y3QkT09CX1JNU0UNCnJlc3VsdHNPdXRGbHVjdCRPT0JfTUFFDQpyZXN1bHRzT3V0Rmx1Y3QkUnNxdWFyZWQNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIGZsdWN0JywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0Rmx1Y3QkT09CX1JNU0UNCnJlc2RIaUFubm95Rml0QUJbJ0FicyBmbHVjdCcsICdNQUUnXSA8LSByZXN1bHRzT3V0Rmx1Y3QkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIGZsdWN0JywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dEZsdWN0JFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBYnNGbHVjdCA8LSByZXN1bHRzT3V0Rmx1Y3QkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yLjl9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0Rmx1Y3QuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dEZsdWN0JGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dEZsdWN0LmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEZsdWN0LmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0Rmx1Y3QuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s0XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgZ2d0aXRsZSgiRmx1Y3R1YXRpb24gc3RyZW5ndGgiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lGbHVjdENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTIuOSwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lGbHVjdENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUZsdWN0Q29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9Mi45LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUZsdWN0Q29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQpmbHVjdFZhciA8LSAiVUFTRmx1Y3RFQ01BMTBFeEJpbiINCg0KYGBgDQoNCiMjIyMjIFJvdWdobmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KIyBSb3VnaG5lc3MNCmlWYXJzIDwtIGMoYWJzVmFyLCBldmVudFZhciwgYW1iVmFyLCAiVUFTUm91Z2hFQ01BMTBFeEJpbiIsICJVQVNSb3VnaEVDTUEwNUV4QmluIiwgIlVBU1JvdWdoRloxMEV4TWF4TFIiLCAiVUFTUm91Z2hGWjA1RXhNYXhMUiIsICJVQVNSb3VnaERXMTBFeE1heExSIiwgIlVBU1JvdWdoRFcwNUV4TWF4TFIiKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpzZWVkcyA8LSBjKDQ3MDEsIDUyMTg3LCAxNjU4OSwgNjUyMTcsIDE2ODkzKQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSA0MDAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjUpDQoNCmBgYA0KDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFJvdWdoIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFJvdWdoJE9PQl9STVNFDQpyZXN1bHRzT3V0Um91Z2gkT09CX01BRQ0KcmVzdWx0c091dFJvdWdoJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFJvdWdoIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRSb3VnaCRPT0JfUk1TRQ0KcmVzdWx0c091dFJvdWdoJE9PQl9NQUUNCnJlc3VsdHNPdXRSb3VnaCRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FicyByb3VnaCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFJvdWdoJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgcm91Z2gnLCAnTUFFJ10gPC0gcmVzdWx0c091dFJvdWdoJE9PQl9NQUUNCnJlc2RIaUFubm95Rml0QUJbJ0FicyByb3VnaCcsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRSb3VnaCRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWJzUm91Z2ggPC0gcmVzdWx0c091dFJvdWdoJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9Mi45fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFJvdWdoLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRSb3VnaCRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRSb3VnaC5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRSb3VnaC5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dFJvdWdoLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbNV0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIGdndGl0bGUoIlJvdWdobmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veVJvdWdoQ29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9Mi45LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veVJvdWdoQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95Um91Z2hDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0yLjksIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95Um91Z2hDb25QZXJtaW1wLnBkZiIpDQp9DQoNCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0Kcm91Z2hWYXIgPC0gIlVBU1JvdWdoRlowNUV4TWF4TFIiDQoNCmBgYA0KDQoNCiMjIyMjIEltcHVsc2l2ZW5lc3MNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQojIEltcHVsc2l2ZW5lc3MNCmlWYXJzIDwtIGMoYWJzVmFyLCBldmVudFZhciwgYW1iVmFyLCAiVUFTSW1wdWxzU0hNQXZnTWF4TFIiLCAiVUFTSW1wdWxzU0hNMDVFeE1heExSIiwgIlVBU0ltcHVsc1NITVBvd0F2Z01heExSIiwgIlVBU0ltcHVsc0xvdWRXWkF2Z01heExSIiwgIlVBU0ltcHVsc0xvdWRXWjA1RXhNYXhMUiIsICJVQVNJbXB1bHNMb3VkV1pQb3dBdmdNYXhMUiIsICJVQVNJbXB1bHNMb3VkV0VDTUFBdmdCaW4iLCAiVUFTSW1wdWxzTG91ZFdFQ01BMDVFeEJpbiIsICJVQVNJbXB1bHNMb3VkV0VDTUFQb3dBdmdCaW4iKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpzZWVkcyA8LSBjKDg0OTUsIDU5ODY3LCA1NDE2LCA5ODQzLCA4NikNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gNTAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjUpDQoNCmBgYA0KDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dEltcHVscyA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRJbXB1bHMkT09CX1JNU0UNCnJlc3VsdHNPdXRJbXB1bHMkT09CX01BRQ0KcmVzdWx0c091dEltcHVscyRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRJbXB1bHMgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dEltcHVscyRPT0JfUk1TRQ0KcmVzdWx0c091dEltcHVscyRPT0JfTUFFDQpyZXN1bHRzT3V0SW1wdWxzJFJzcXVhcmVkDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgaW1wdWxzJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0SW1wdWxzJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgaW1wdWxzJywgJ01BRSddIDwtIHJlc3VsdHNPdXRJbXB1bHMkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIGltcHVscycsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRJbXB1bHMkUnNxdWFyZWQNCnJlc2RIaUFubm95UGVybUltcEFCJEFic0ltcHVscyA8LSByZXN1bHRzT3V0SW1wdWxzJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9NH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRJbXB1bHMuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dEltcHVscyRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRJbXB1bHMuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbNl0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIGdndGl0bGUoIkltcHVsc2l2ZW5lc3MiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lJbXB1bHNDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD00LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUltcHVsc0NvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUltcHVsc0NvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95SW1wdWxzQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQppbXB1bHNWYXIgPC0gIlVBU0ltcHVsc0xvdWRXWkF2Z01heExSIg0KDQpgYGANCg0KIyMjIyBTUU0gYW5kIGxvdWRuZXNzIGNvbXBhcmlzb24NCg0KTm93IHRoZSBoaWdoZXN0IGltcG9ydGFuY2UgU1FNcyBhcmUgcmFua2VkIGFnYWluc3QgZWFjaCBvdGhlciwgY29udHJvbGxpbmcgZm9yIFVBUyBsb3VkbmVzcyBhbmQgYW1iaWVudCBMQWVxLg0KDQojIyMjIyBJbmNsdWRlIHRvbmFsIGxvdWRuZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFic1ZhciwgZXZlbnRWYXIsIGFtYlZhciwgc2hhcnBWYXIsIHRvbkxkVmFyLCBmbHVjdFZhciwgcm91Z2hWYXIsIGltcHVsc1ZhcikNCmRWYXIgPC0gImRIaWdoQW5ub3lQYyINCg0Kc2VlZHMgPC0gYyg3MDQ5OCwgNCwgMTQ5ODYsIDQ1MywgODY0KQ0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAxNTAxDQptdHJ5IDwtIDMNCg0KYGBgDQoNCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0U1FNczEgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0U1FNczEkT09CX1JNU0UNCnJlc3VsdHNPdXRTUU1zMSRPT0JfTUFFDQpyZXN1bHRzT3V0U1FNczEkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0U1FNczEgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMxJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczEkT09CX01BRQ0KcmVzdWx0c091dFNRTXMxJFJzcXVhcmVkDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgU1FNcyBpbmMgdG9uYWwgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNRTXMxJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgU1FNcyBpbmMgdG9uYWwgbG91ZCcsICdNQUUnXSA8LSByZXN1bHRzT3V0U1FNczEkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIFNRTXMgaW5jIHRvbmFsIGxvdWQnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0U1FNczEkUnNxdWFyZWQNCnJlc2RIaUFubm95UGVybUltcEFCJEFic1NRTXMxIDwtIHJlc3VsdHNPdXRTUU1zMSRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTIuNH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRTUU1zMS5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0U1FNczEkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0U1FNczEuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0U1FNczEuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRTUU1zMS5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzddLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKCUgaGlnaGx5IGFubm95ZWQpIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoeWxpbT1jKDAsIDMwKSkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFic1NRTXNUb25MZENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBYnNTUU1zVG9uTGRDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBYnNTUU1zVG9uTGRDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0yLjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWJzU1FNc1RvbkxkQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KIyMjIyMgRXhjbHVkZSB0b25hbCBsb3VkbmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhYnNWYXIsIGV2ZW50VmFyLCBhbWJWYXIsIHNoYXJwVmFyLCB0b25hbFZhciwgZmx1Y3RWYXIsIHJvdWdoVmFyLCBpbXB1bHNWYXIpDQpkVmFyIDwtICJkSGlnaEFubm95UGMiDQoNCnNlZWRzIDwtIGMoNTQ2LCA1NzIwMywgMjcwODM1LCA2MDU5MiwgODA5NCkNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gNDAwMQ0KbXRyeSA8LSAzDQoNCmBgYA0KDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFNRTXMyIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczIkT09CX01BRQ0KcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNRTXMyIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTUU1zMiRPT0JfUk1TRQ0KcmVzdWx0c091dFNRTXMyJE9PQl9NQUUNCnJlc3VsdHNPdXRTUU1zMiRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnQWJzIFNRTXMgbm8gdG9uYWwgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgU1FNcyBubyB0b25hbCBsb3VkJywgJ01BRSddIDwtIHJlc3VsdHNPdXRTUU1zMiRPT0JfTUFFDQpyZXNkSGlBbm5veUZpdEFCWydBYnMgU1FNcyBubyB0b25hbCBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBYnNTUU1zMiA8LSByZXN1bHRzT3V0U1FNczIkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yLjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0U1FNczIuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFNRTXMyJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFNRTXMyLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFNRTXMyLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0U1FNczIuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s3XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCAzMCkpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBYnNTUU1zTm9Ub25MZENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBYnNTUU1zTm9Ub25MZENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFic1NRTXNOb1RvbkxkQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9Mi40LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFic1NRTXNOb1RvbkxkQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KIyMjIyBQc3ljaG9hY291c3RpYyBhbm5veWFuY2UgbWV0cmljcw0KDQojIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFtYlZhciwgIlVBU1BzeWNoQW5ub3lXaWRtYW5uIiwgIlVBU1BzeWNoQW5ub3lNb3JlIiwgIlVBU1BzeWNoQW5ub3lEaSIsICJVQVNQc3ljaEFubm95VG9yaWphIiwgIlVBU1BzeWNoQW5ub3lXaWxsZW1zZW4iLCAiVUFTUHN5Y2hBbm5veUJvdWNoZXIiKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpzZWVkcyA8LSBjKDQ4NjUxLCA0NSwgNzg1MTIzLCA2NSwgNTE2MykNCg0KDQpgYGANCg0KIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gNDAwMQ0KbXRyeSA8LSA0DQoNCmBgYA0KDQojIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFBBIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFBBJE9PQl9STVNFDQpyZXN1bHRzT3V0UEEkT09CX01BRQ0KcmVzdWx0c091dFBBJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFBBIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRQQSRPT0JfUk1TRQ0KcmVzdWx0c091dFBBJE9PQl9NQUUNCnJlc3VsdHNPdXRQQSRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnUHN5Y2hvYWNvdXN0aWMgYW5ub3lhbmNlJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0UEEkT09CX1JNU0UNCnJlc2RIaUFubm95Rml0QUJbJ1BzeWNob2Fjb3VzdGljIGFubm95YW5jZScsICdNQUUnXSA8LSByZXN1bHRzT3V0UEEkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnUHN5Y2hvYWNvdXN0aWMgYW5ub3lhbmNlJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFBBJFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBYnNQQSA8LSByZXN1bHRzT3V0UEEkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTIuNH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRQQS5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0UEEkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0UEEuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0UEEuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRQQS5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzEwXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCA2MCkpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBYnNQQUNvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBYnNQQUNvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFic1BBQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9Mi40LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFic1BBQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KIyMjIEFsbCB2YXJpYWJsZXMgKGFic29sdXRlIGFuZCBkaWZmZXJlbmNlKQ0KDQojIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIG5hbWVzKHN0aW1EYXRhTnVtKVt3aGljaChuYW1lcyhzdGltRGF0YU51bSkgPT0gJ1VBU0V2ZW50cycpOndoaWNoKG5hbWVzKHN0aW1EYXRhTnVtKSA9PSAnVUFTUHN5Y2hBbm5veUJvdWNoZXInKV0NCmlWYXJzIDwtIGlWYXJzWyEgaVZhcnMgJWluJSAnU05SbGV2ZWwnXQ0KaVZhcnMgPC0gYyhpVmFycywNCiAgICAgICAgICAgbmFtZXMoc3RpbURhdGFOdW0pW3doaWNoKGNvbG5hbWVzKHN0aW1EYXRhTnVtKT09J0xBZXFMQUY5MGRpZmYnKToNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaChjb2xuYW1lcyhzdGltRGF0YU51bSk9PSdkUHN5Y2hBbm5veUJvdWNoZXInKV0sICdTTlJsZXZlbCcpDQpkVmFyIDwtICJkSGlnaEFubm95UGMiDQoNCnNlZWRzIDwtIGMoMiwgMzEyLCAxODk3LCA0NjU5NzgsIDgyMTY1OSkNCg0KYGBgDQoNCiMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAxNTAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8zLjUpDQoNCmBgYA0KDQojIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0QWJzRGlmZnMgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0QWJzRGlmZnMkT09CX1JNU0UNCnJlc3VsdHNPdXRBYnNEaWZmcyRPT0JfTUFFDQpyZXN1bHRzT3V0QWJzRGlmZnMkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KDQpyZXN1bHRzT3V0QWJzRGlmZnMgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dEFic0RpZmZzJE9PQl9STVNFDQpyZXN1bHRzT3V0QWJzRGlmZnMkT09CX01BRQ0KcmVzdWx0c091dEFic0RpZmZzJFJzcXVhcmVkDQpgYGANCg0KYGBge3J9DQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCB2YXJzJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0QWJzRGlmZnMkT09CX1JNU0UNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCB2YXJzJywgJ01BRSddIDwtIHJlc3VsdHNPdXRBYnNEaWZmcyRPT0JfTUFFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgdmFycycsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRBYnNEaWZmcyRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWxsVmFycyA8LSByZXN1bHRzT3V0QWJzRGlmZnMkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9MjZ9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dEFic0RpZmZzJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dEFic0RpZmZzLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEFic0RpZmZzLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s5XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbFZhcnNDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD0yNiwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxWYXJzQ29uUGVybWltcC5zdmciKQ0KDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbFZhcnNDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD0yNiwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxWYXJzQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9MjJ9DQoNCiMgUGxvdCBvbmx5IHBvc2l0aXZlIHZhbHVlcw0KDQpyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wUHR2IDwtIHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXAgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbignTWV0cmljJykgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcl9pZihpcy5udW1lcmljLCBhbGxfdmFycyguID4gMCkpIHw+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoJ01ldHJpYycpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXBQdHYpICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXBQdHYpLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dEFic0RpZmZzLmNvbmltcFB0dikpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbOV0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxWYXJzQ29uUGVybWltcFB0di5zdmciLCB3aWR0aD04LCBoZWlnaHQ9MjIsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWxsVmFyc0NvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbFZhcnNDb25QZXJtaW1wUHR2LnBkZiIsIHdpZHRoPTgsIGhlaWdodD0yMiwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxWYXJzQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD03fQ0KDQojIFBsb3Qgb25seSB2YWx1ZXMgd2l0aGluIDElIG9mIHRoZSBtYXhpbXVtDQoNCnJlc3VsdHNPdXRBYnNEaWZmcy5jb25pbXAxcGMgPC0gcmVzdWx0c091dEFic0RpZmZzLmNvbmltcCB8Pg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKCdNZXRyaWMnKSB8Pg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyX2lmKGlzLm51bWVyaWMsIGFsbF92YXJzKC4gPiBtYXgocmVzdWx0c091dEFic0RpZmZzLmNvbmltcCkvMTAwKSkgfD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygnTWV0cmljJykNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dEFic0RpZmZzLmNvbmltcDFwYykgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEFic0RpZmZzLmNvbmltcDFwYyksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0QWJzRGlmZnMuY29uaW1wMXBjKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s5XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbFZhcnNDb25QZXJtaW1wMXBjLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD03LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFsbFZhcnNDb25QZXJtaW1wMXBjLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxWYXJzQ29uUGVybWltcDFwYy5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NywgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxWYXJzQ29uUGVybWltcDFwYy5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQphbGxWYXIgPC0gIlVBU0xvdWRFQ01BUG93QXZnQmluIg0KDQpgYGANCg0KIyMjIGRTUU0gYW5hbHlzaXMNCg0KIyMjIyBJbmRpdmlkdWFsIFNRTXMNCg0KIyMjIyMgZFNoYXJwbmVzcw0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhbGxWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJkU2hhcnBBdXJJU08zUG93QXZnQmluIiwgImRTaGFycEF1cklTTzMwNUV4QmluIiwgImRTaGFycEF1clNITVBvd0F2Z0JpbiIsICJkU2hhcnBBdXJTSE0wNUV4QmluIiwgImRUb25TaHBBdXJTSE1Qb3dBdmdCaW4iLCAiZFRvblNocEF1clNITTA1RXhCaW4iLCAiUGFydFRvblNocEF1clNITVBvd0F2Z0JpbiIsDQogICAgICAgICAgICJQYXJ0VG9uU2hwQXVyU0hNMDVFeEJpbiIsICJVQVNTaGFycEF1cklTTzNQb3dBdmdCaW4iLCAiVUFTU2hhcnBBdXJJU08zMDVFeEJpbiIsICJVQVNTaGFycEF1clNITVBvd0F2Z0JpbiIsICJVQVNTaGFycEF1clNITTA1RXhCaW4iLCAiVUFTU2hhcnBBdXJJU08xUG93QXZnQmluIiwgIlVBU1NoYXJwQXVySVNPMTA1RXhCaW4iLCAiVUFTU2hhcnB2QklTTzFQb3dBdmdCaW4iLCAiVUFTU2hhcnB2QklTTzEwNUV4QmluIiwgIlVBU1NoYXJwRElOUG93QXZnQmluIiwgIlVBU1NoYXJwRElOMDVFeEJpbiIsICJVQVNTaGFycEF1cklTTzFNZWRCaW4iLA0KICAgICAgICAgIlVBU1RvblNocEF1clNITVBvd0F2Z0JpbiIsICJVQVNUb25TaHBBdXJTSE0wNUV4QmluIikNCmRWYXIgPC0gImRIaWdoQW5ub3lQYyINCg0Kc2VlZHMgPC0gYyg4NDE5NCwgOTA1LCA2NDgxNSwgOTI4MDU0LCA2MjUwOTEsIDU4MjAzMSkNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMjUxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8yLjI1KQ0KDQpgYGANCg0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSAxMA0KDQpyZXN1bHRzT3V0U2hhcnAgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0U2hhcnAkT09CX1JNU0UNCnJlc3VsdHNPdXRTaGFycCRPT0JfTUFFDQpyZXN1bHRzT3V0U2hhcnAkUnNxdWFyZWQNCg0KYGBgDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNoYXJwIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTaGFycCRPT0JfUk1TRQ0KcmVzdWx0c091dFNoYXJwJE9PQl9NQUUNCnJlc3VsdHNPdXRTaGFycCRSc3F1YXJlZA0KDQpgYGANCg0KDQpgYGB7cn0NCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIHNoYXJwJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0U2hhcnAkT09CX1JNU0UNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCBzaGFycCcsICdNQUUnXSA8LSByZXN1bHRzT3V0U2hhcnAkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIHNoYXJwJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFNoYXJwJFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBbGxTaGFycCA8LSByZXN1bHRzT3V0U2hhcnAkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD01fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFNoYXJwLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRTaGFycCRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRTaGFycC5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRTaGFycC5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dFNoYXJwLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbMl0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIGdndGl0bGUoIkFsbCBzaGFycG5lc3MiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCgpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxTaGFycENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTUsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWxsU2hhcnBDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxTaGFycENvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTUsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWxsU2hhcnBDb25QZXJtaW1wLnBkZiIpDQp9DQoNCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0KYWxsU2hhcnBWYXIgPC0gImRTaGFycEF1clNITVBvd0F2Z0JpbiINCg0KYGBgDQoNCg0KIyMjIyMgZFRvbmFsIGxvdWRuZXNzIGFuZCBkdG9uYWxpdHkNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYWxsVmFyLCBldmVudFZhciwgYW1iVmFyLCAiZFRvbmFsRUNNQUF2Z01heExSIiwgImRUb25hbFNITUludDA1RXhNYXhMUiIsICJkVG9uYWxTSE1JbnRBdmdNYXhMUiIsICJkVG9uYWxFQ01BMDVFeE1heExSIiwgImRUb25hbEF3U0hNQXZnTWF4TFIiLAkiZFRvbmFsQXdTSE0wNUV4TWF4TFIiLAkiZFRvbmFsQXdTSE1JbnRBdmdNYXhMUiIsIAkiZFRvbmFsQXdTSE1JbnQwNUV4TWF4TFIiLCAiZFRvbkxkRUNNQVBvd0F2Z0JpbiIsICJkVG9uTGRFQ01BMDVFeEJpbiIsICJkVG9uU2hwQXVyU0hNUG93QXZnQmluIiwNCiAgICAgICAgICAgImRUb25TaHBBdXJTSE0wNUV4QmluIiwgIlBhcnRUb25MZFNITVBvd0F2Z0JpbiIsICJVQVNUb25hbEVDTUFBdmdNYXhMUiIsICJVQVNUb25hbFNITUludDA1RXhNYXhMUiIsICJVQVNUb25hbFNITUludEF2Z01heExSIiwgIlVBU1RvbmFsRUNNQTA1RXhNYXhMUiIsICJVQVNUb25hbEF3U0hNQXZnTWF4TFIiLAkiVUFTVG9uYWxBd1NITTA1RXhNYXhMUiIsCSJVQVNUb25hbEF3U0hNSW50QXZnTWF4TFIiLCAJIlVBU1RvbmFsQXdTSE1JbnQwNUV4TWF4TFIiLCAiVUFTVG9uTGRFQ01BUG93QXZnQmluIiwgIlVBU1RvbkxkRUNNQTA1RXhCaW4iLCAiVUFTVG9uYWxBdXJBdmdNYXhMUiIsICJVQVNUb25hbEF1cjA1RXhNYXhMUiIsICJVQVNUb25hbEF1cjEwRXhNYXhMUiIsDQogICAgICAgICAiVUFTVG9uU2hwQXVyU0hNUG93QXZnQmluIiwgIlVBU1RvblNocEF1clNITTA1RXhCaW4iKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpgYGANCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpzZWVkcyA8LSBjKDU2MTY4NCwgMTA0Nzk4LCAxNTM2LCA0OCwgNDg1NjEpDQoNCmBgYA0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSA1MDENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzIuMjUpDQoNCmBgYA0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eSB3aXRoIHRvbmFsIGxvdWRuZXNzDQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFRvbmFsMSA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRUb25hbDEkT09CX1JNU0UNCnJlc3VsdHNPdXRUb25hbDEkT09CX01BRQ0KcmVzdWx0c091dFRvbmFsMSRSc3F1YXJlZA0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eSB3aXRoIHRvbmFsIGxvdWRuZXNzDQoNCnJlc3VsdHNPdXRUb25hbDEgPC0gbXVsdGlfY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHMsIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFRvbmFsMSRPT0JfUk1TRQ0KcmVzdWx0c091dFRvbmFsMSRPT0JfTUFFDQpyZXN1bHRzT3V0VG9uYWwxJFJzcXVhcmVkDQoNCmBgYA0KDQpgYGB7cn0NCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIHRvbmFsIGluYyBsb3VkJywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0VG9uYWwxJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgdG9uYWwgaW5jIGxvdWQnLCAnTUFFJ10gPC0gcmVzdWx0c091dFRvbmFsMSRPT0JfTUFFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgdG9uYWwgaW5jIGxvdWQnLCAnUnNxdWFyZWQnXSA8LSByZXN1bHRzT3V0VG9uYWwxJFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBbGxUb25hbDEgPC0gcmVzdWx0c091dFRvbmFsMSRjb25kaXRpb25hbF9wZXJtaW1wDQoNCmBgYA0KDQojIyMjIyMgUGxvdCByZXN1bHRzDQoNCmBgYHtyLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTZ9DQoNCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRUb25hbDEuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFRvbmFsMSRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRUb25hbDEuY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0VG9uYWwxLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0VG9uYWwxLmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbM10sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIGdndGl0bGUoIkFsbCB0b25hbGl0eSBpbmMuIHRvbmFsIGxvdWRuZXNzIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoeWxpbT1jKDAsIDUwKSkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbFRvbmFsTGRDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD02LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFsbFRvbmFsTGRDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxUb25hbExkQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NiwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxUb25hbExkQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQphbGxUb25MZFZhciA8LSAiVUFTVG9uTGRFQ01BUG93QXZnQmluIg0KDQpgYGANCg0KIyMjIyMgZFRvbmFsaXR5IHdpdGhvdXQgZHRvbmFsIGxvdWRuZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQppVmFycyA8LSBjKGFsbFZhciwgZXZlbnRWYXIsIGFtYlZhciwgImRUb25hbEVDTUFBdmdNYXhMUiIsICJkVG9uYWxTSE1JbnQwNUV4TWF4TFIiLCAiZFRvbmFsU0hNSW50QXZnTWF4TFIiLCAiZFRvbmFsRUNNQTA1RXhNYXhMUiIsICJkVG9uYWxBd1NITUF2Z01heExSIiwJImRUb25hbEF3U0hNMDVFeE1heExSIiwJImRUb25hbEF3U0hNSW50QXZnTWF4TFIiLCAJImRUb25hbEF3U0hNSW50MDVFeE1heExSIiwgIlVBU1RvbmFsRUNNQUF2Z01heExSIiwgIlVBU1RvbmFsU0hNSW50MDVFeE1heExSIiwgIlVBU1RvbmFsU0hNSW50QXZnTWF4TFIiLCAiVUFTVG9uYWxFQ01BMDVFeE1heExSIiwgIlVBU1RvbmFsQXdTSE1BdmdNYXhMUiIsCSJVQVNUb25hbEF3U0hNMDVFeE1heExSIiwJIlVBU1RvbmFsQXdTSE1JbnRBdmdNYXhMUiIsICJVQVNUb25hbEF3U0hNSW50MDVFeE1heExSIiwJIlVBU1RvbmFsQXVyQXZnTWF4TFIiLCAiVUFTVG9uYWxBdXIwNUV4TWF4TFIiLCAiVUFTVG9uYWxBdXIxMEV4TWF4TFIiKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpzZWVkcyA8LSBjKDQxMDg2NSwgMjk1NCwgNzA4MTIsIDIwMywgNzk4NCkNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gNTAxDQptdHJ5IDwtIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjI1KQ0KDQpgYGANCg0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRUb25hbDIgPC0gY3JmUmVnKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0VG9uYWwyJE9PQl9STVNFDQpyZXN1bHRzT3V0VG9uYWwyJE9PQl9NQUUNCnJlc3VsdHNPdXRUb25hbDIkUnNxdWFyZWQNCg0KYGBgDQoNClRyYWluIG11bHRpcGxlIHNlZWRzIG1vZGVsDQoNCmBgYHtyfQ0KIyBUb25hbGl0eQ0KDQpyZXN1bHRzT3V0VG9uYWwyIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRUb25hbDIkT09CX1JNU0UNCnJlc3VsdHNPdXRUb25hbDIkT09CX01BRQ0KcmVzdWx0c091dFRvbmFsMiRSc3F1YXJlZA0KDQpgYGANCg0KDQpgYGB7cn0NCg0KIyBzdG9yZSByZXN1bHRzDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgdG9uYWwgbm8gbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFRvbmFsMiRPT0JfUk1TRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIHRvbmFsIG5vIGxvdWQnLCAnTUFFJ10gPC0gcmVzdWx0c091dFRvbmFsMiRPT0JfTUFFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgdG9uYWwgbm8gbG91ZCcsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRUb25hbDIkUnNxdWFyZWQNCnJlc2RIaUFubm95UGVybUltcEFCJEFsbFRvbmFsMiA8LSByZXN1bHRzT3V0VG9uYWwyJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9NC44fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dFRvbmFsMi5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0VG9uYWwyJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFRvbmFsMi5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRUb25hbDIuY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRUb25hbDIuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1szXSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgZ2d0aXRsZSgiQWxsIHRvbmFsaXR5IHcvbyB0b25hbCBsb3VkbmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCAxMDApKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95QWxsVG9uYWxDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD00LjgsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWxsVG9uYWxDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxUb25hbENvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTQuOCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxUb25hbENvblBlcm1pbXAucGRmIikNCn0NCg0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQphbGxUb25hbFZhciA8LSAiVUFTVG9uYWxBd1NITUludDA1RXhNYXhMUiINCg0KYGBgDQoNCiMjIyMjIGRGbHVjdHVhdGlvbiBzdHJlbmd0aA0KDQojIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KIyBGbHVjdHVhdGlvbiBzdHJlbmd0aA0KaVZhcnMgPC0gYyhhbGxWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJkRmx1Y3RFQ01BMTBFeEJpbiIsICJkRmx1Y3RFQ01BMDVFeEJpbiIsICJkRmx1Y3RPVjEwRXhNYXhMUiIsICJkRmx1Y3RPVjA1RXhNYXhMUiIsICJVQVNGbHVjdE9sZFNITTEwRXhCaW4iLCAiVUFTRmx1Y3RPbGRTSE0wNUV4QmluIiwgIlVBU0ZsdWN0RUNNQTEwRXhCaW4iLCAiVUFTRmx1Y3RFQ01BMDVFeEJpbiIsICJVQVNGbHVjdEZaMTBFeE1heExSIiwgIlVBU0ZsdWN0RlowNUV4TWF4TFIiLCAiVUFTRmx1Y3RPVjEwRXhNYXhMUiIsICJVQVNGbHVjdE9WMDVFeE1heExSIikNCmRWYXIgPC0gImRIaWdoQW5ub3lQYyINCg0Kc2VlZHMgPC0gYyg0MTg2NTcsIDg0LCAxNjMwLCAxODY1OSwgMzY4NykNCg0KYGBgDQoNCg0KIyMjIyMjIEh5cGVycGFyYW1ldGVyIHR1bmluZw0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQoNCnAgPC0gbXRyeVR1bmUoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLA0KICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KU2VsZWN0ZWQgaHlwZXJwYXJhbWV0ZXJzDQoNCmBgYHtyfQ0KDQpudHJlZSA8LSAyNTENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzEuMjUpDQoNCmBgYA0KDQojIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRGbHVjdCA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRGbHVjdCRPT0JfUk1TRQ0KcmVzdWx0c091dEZsdWN0JE9PQl9NQUUNCnJlc3VsdHNPdXRGbHVjdCRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRGbHVjdCA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0Rmx1Y3QkT09CX1JNU0UNCnJlc3VsdHNPdXRGbHVjdCRPT0JfTUFFDQpyZXN1bHRzT3V0Rmx1Y3QkUnNxdWFyZWQNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIGZsdWN0JywgJ1JNU0UnXSA8LSByZXN1bHRzT3V0Rmx1Y3QkT09CX1JNU0UNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCBmbHVjdCcsICdNQUUnXSA8LSByZXN1bHRzT3V0Rmx1Y3QkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIGZsdWN0JywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dEZsdWN0JFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBbGxGbHVjdCA8LSByZXN1bHRzT3V0Rmx1Y3QkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD00fQ0KcGFyKG1haT1jKDAsMywwLDApKQ0KDQojIHBsb3QgY29uZGl0aW9uYWwgaW1wb3J0YW5jZQ0KcmVzdWx0c091dEZsdWN0LmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRGbHVjdCRjb25kaXRpb25hbF9wZXJtaW1wLCBkZXNjKHJvd19udW1iZXIoKSkpDQoNCnBCYXIgPC0gZ2dwbG90KHJlc3VsdHNPdXRGbHVjdC5jb25pbXApICsgZ2VvbV9jb2woYWVzKHg9ZmFjdG9yKHJvd25hbWVzKHJlc3VsdHNPdXRGbHVjdC5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dEZsdWN0LmNvbmltcCkpLCB5PUNvbmRQZXJtSW1wKSwgZmlsbD1teWNvbG91cnNbNF0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIGdndGl0bGUoIkFsbCBmbHVjdHVhdGlvbiBzdHJlbmd0aCIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbEZsdWN0Q29uUGVybWltcC5zdmciLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxGbHVjdENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbEZsdWN0Q29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxGbHVjdENvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNClNlbGVjdGVkIG1ldHJpYw0KDQpgYGB7cn0NCg0KYWxsRmx1Y3RWYXIgPC0gIlVBU0ZsdWN0RUNNQTEwRXhCaW4iDQoNCmBgYA0KDQojIyMjIyBkUm91Z2huZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQojIFJvdWdobmVzcw0KaVZhcnMgPC0gYyhhbGxWYXIsIGV2ZW50VmFyLCBhbWJWYXIsICJkUm91Z2hFQ01BMTBFeEJpbiIsICJkUm91Z2hFQ01BMDVFeEJpbiIsICJkUm91Z2hGWjEwRXhNYXhMUiIsICJkUm91Z2hGWjA1RXhNYXhMUiIsICJVQVNSb3VnaEVDTUExMEV4QmluIiwgIlVBU1JvdWdoRUNNQTA1RXhCaW4iLCAiVUFTUm91Z2hGWjEwRXhNYXhMUiIsICJVQVNSb3VnaEZaMDVFeE1heExSIiwgIlVBU1JvdWdoRFcxMEV4TWF4TFIiLCAiVUFTUm91Z2hEVzA1RXhNYXhMUiIpDQpkVmFyIDwtICJkSGlnaEFubm95UGMiDQoNCnNlZWRzIDwtIGMoNjk4NTEsIDg1MTA5LCA0MTA5ODYsIDE1NjMsIDg5NikNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMTUwMQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS4yNSkNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFJvdWdoIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFJvdWdoJE9PQl9STVNFDQpyZXN1bHRzT3V0Um91Z2gkT09CX01BRQ0KcmVzdWx0c091dFJvdWdoJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFJvdWdoIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRSb3VnaCRPT0JfUk1TRQ0KcmVzdWx0c091dFJvdWdoJE9PQl9NQUUNCnJlc3VsdHNPdXRSb3VnaCRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCByb3VnaCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFJvdWdoJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgcm91Z2gnLCAnTUFFJ10gPC0gcmVzdWx0c091dFJvdWdoJE9PQl9NQUUNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCByb3VnaCcsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRSb3VnaCRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWxsUm91Z2ggPC0gcmVzdWx0c091dFJvdWdoJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIyBQbG90IHJlc3VsdHMNCg0KYGBge3IsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9NH0NCnBhcihtYWk9YygwLDMsMCwwKSkNCg0KIyBwbG90IGNvbmRpdGlvbmFsIGltcG9ydGFuY2UNCnJlc3VsdHNPdXRSb3VnaC5jb25pbXAgPC0gYXJyYW5nZShyZXN1bHRzT3V0Um91Z2gkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0Um91Z2guY29uaW1wKSArIGdlb21fY29sKGFlcyh4PWZhY3Rvcihyb3duYW1lcyhyZXN1bHRzT3V0Um91Z2guY29uaW1wKSwgbGV2ZWxzPXJvd25hbWVzKHJlc3VsdHNPdXRSb3VnaC5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzVdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKCUgaGlnaGx5IGFubm95ZWQpIikgKyBnZ3RpdGxlKCJBbGwgcm91Z2huZXNzIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpKSArIGNvb3JkX2ZsaXAoKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95QWxsUm91Z2hDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD00LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFsbFJvdWdoQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95QWxsUm91Z2hDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD00LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFsbFJvdWdoQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQoNCmBgYA0KDQpTZWxlY3RlZCBtZXRyaWMNCg0KYGBge3J9DQoNCmFsbFJvdWdoVmFyIDwtICJkUm91Z2hGWjA1RXhNYXhMUiINCg0KYGBgDQoNCiMjIyMjIGRJbXB1bHNpdmVuZXNzDQoNCiMjIyMjIyBTZXQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KIyBJbXB1bHNpdmVuZXNzDQppVmFycyA8LSBjKGFsbFZhciwgZXZlbnRWYXIsIGFtYlZhciwgImRJbXB1bHNTSE1BdmdNYXhMUiIsICJkSW1wdWxzU0hNMDVFeE1heExSIiwgImRJbXB1bHNTSE1Qb3dBdmdNYXhMUiIsDQogICAgICAgICAgICJkSW1wdWxzTG91ZFdaQXZnTWF4TFIiLCAiZEltcHVsc0xvdWRXWjA1RXhNYXhMUiIsICJkSW1wdWxzTG91ZFdaUG93QXZnTWF4TFIiLA0KICAgICAgICAgICAiZEltcHVsc0xvdWRXRUNNQUF2Z0JpbiIsICJkSW1wdWxzTG91ZFdFQ01BMDVFeEJpbiIsICJkSW1wdWxzTG91ZFdFQ01BUG93QXZnQmluIiwgIlVBU0ltcHVsc1NITUF2Z01heExSIiwgIlVBU0ltcHVsc1NITTA1RXhNYXhMUiIsICJVQVNJbXB1bHNTSE1Qb3dBdmdNYXhMUiIsICJVQVNJbXB1bHNMb3VkV1pBdmdNYXhMUiIsICJVQVNJbXB1bHNMb3VkV1owNUV4TWF4TFIiLCAiVUFTSW1wdWxzTG91ZFdaUG93QXZnTWF4TFIiLCAiVUFTSW1wdWxzTG91ZFdFQ01BQXZnQmluIiwgIlVBU0ltcHVsc0xvdWRXRUNNQTA1RXhCaW4iLCAiVUFTSW1wdWxzTG91ZFdFQ01BUG93QXZnQmluIikNCmRWYXIgPC0gImRIaWdoQW5ub3lQYyINCg0Kc2VlZHMgPC0gYyg0MTg2NTksIDc4MDUsIDM4NDc1LCA2NTgzNCwgMTY1MykNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMTUwMQ0KbXRyeSA8LSBhcy5pbnRlZ2VyKGxlbmd0aChpVmFycykvMS43NSkNCg0KYGBgDQoNCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpUcmFpbiBwcmVsaW1pbmFyeSBtb2RlbA0KDQpgYGB7cn0NCg0KbnBlcm0gPC0gNQ0KDQpyZXN1bHRzT3V0SW1wdWxzIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dEltcHVscyRPT0JfUk1TRQ0KcmVzdWx0c091dEltcHVscyRPT0JfTUFFDQpyZXN1bHRzT3V0SW1wdWxzJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dEltcHVscyA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0SW1wdWxzJE9PQl9STVNFDQpyZXN1bHRzT3V0SW1wdWxzJE9PQl9NQUUNCnJlc3VsdHNPdXRJbXB1bHMkUnNxdWFyZWQNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCBpbXB1bHMnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRJbXB1bHMkT09CX1JNU0UNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCBpbXB1bHMnLCAnTUFFJ10gPC0gcmVzdWx0c091dEltcHVscyRPT0JfTUFFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgaW1wdWxzJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dEltcHVscyRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWxsSW1wdWxzIDwtIHJlc3VsdHNPdXRJbXB1bHMkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD01LjZ9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCA8LSBhcnJhbmdlKHJlc3VsdHNPdXRJbXB1bHMkY29uZGl0aW9uYWxfcGVybWltcCwgZGVzYyhyb3dfbnVtYmVyKCkpKQ0KDQpwQmFyIDwtIGdncGxvdChyZXN1bHRzT3V0SW1wdWxzLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dEltcHVscy5jb25pbXApLCBsZXZlbHM9cm93bmFtZXMocmVzdWx0c091dEltcHVscy5jb25pbXApKSwgeT1Db25kUGVybUltcCksIGZpbGw9bXljb2xvdXJzWzZdLCB3aWR0aD0wLjUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9IkNvbmRpdGlvbmFsIHZhcmlhYmxlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgKCUgaGlnaGx5IGFubm95ZWQpIikgKyBnZ3RpdGxlKCJBbGwgaW1wdWxzaXZlbmVzcyIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKCkNCnBCYXINCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbEltcHVsc0NvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTUuNiwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxJbXB1bHNDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxJbXB1bHNDb25QZXJtaW1wLnBkZiIsIHdpZHRoPTgsIGhlaWdodD01LjYsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWxsSW1wdWxzQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KU2VsZWN0ZWQgbWV0cmljDQoNCmBgYHtyfQ0KDQphbGxJbXB1bHNWYXIgPC0gIlVBU0ltcHVsc0xvdWRXWkF2Z01heExSIg0KDQpgYGANCg0KIyMjIyBkU1FNIGFuZCBsb3VkbmVzcyBjb21wYXJpc29uDQoNCk5vdyB0aGUgaGlnaGVzdCBpbXBvcnRhbmNlIGRTUU1zIGFyZSByYW5rZWQgYWdhaW5zdCBlYWNoIG90aGVyLCBjb250cm9sbGluZyBmb3IgbG91ZG5lc3MgZGlmZmVyZW5jZS4NCg0KIyMjIyMgSW5jbHVkZSBkdG9uYWwgbG91ZG5lc3MNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYWxsVmFyLCBldmVudFZhciwgYW1iVmFyLCBhbGxTaGFycFZhciwgYWxsVG9uTGRWYXIsIGFsbEZsdWN0VmFyLCBhbGxSb3VnaFZhciwgYWxsSW1wdWxzVmFyKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpzZWVkcyA8LSBjKDk4NDY1LCA1NDE2MywgNjU0MSwgMzY0ODUsIDg0OTY3NSkNCg0KYGBgDQoNCiMjIyMjIyBIeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KDQpwIDwtIG10cnlUdW5lKGRhdGFJbj1zdGltRGF0YU51bSwgaVZhcnM9aVZhcnMsIGRWYXI9ZFZhciwgc2VlZHM9c2VlZHNbMToyXSwNCiAgICAgICAgICAgICBudHJlZXM9bnRyZWVzLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCnANCg0KYGBgDQoNClNlbGVjdGVkIGh5cGVycGFyYW1ldGVycw0KDQpgYGB7cn0NCg0KbnRyZWUgPC0gMTAwMQ0KbXRyeSA8LSAzDQoNCmBgYA0KDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFNRTXMxIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMxJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczEkT09CX01BRQ0KcmVzdWx0c091dFNRTXMxJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNRTXMxIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTUU1zMSRPT0JfUk1TRQ0KcmVzdWx0c091dFNRTXMxJE9PQl9NQUUNCnJlc3VsdHNPdXRTUU1zMSRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIFNRTXMgaW5jIHRvbmFsIGxvdWQnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRTUU1zMSRPT0JfUk1TRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIFNRTXMgaW5jIHRvbmFsIGxvdWQnLCAnTUFFJ10gPC0gcmVzdWx0c091dFNRTXMxJE9PQl9NQUUNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCBTUU1zIGluYyB0b25hbCBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFNRTXMxJFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBbGxTUU1zMSA8LSByZXN1bHRzT3V0U1FNczEkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yLjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0U1FNczEuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFNRTXMxJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFNRTXMxLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFNRTXMxLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0U1FNczEuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s3XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCA0MCkpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxTUU1zVG9uTGRDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD0yLjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWxsU1FNc1RvbkxkQ29uUGVybWltcC5zdmciKQ0KICANCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95QWxsU1FNc1RvbkxkQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9Mi40LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFsbFNRTXNUb25MZENvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNCiMjIyMjIEV4Y2x1ZGUgdG9uYWwgbG91ZG5lc3MNCg0KIyMjIyMjIFNldCB2YXJpYWJsZXMNCg0KYGBge3J9DQoNCmlWYXJzIDwtIGMoYWxsVmFyLCBldmVudFZhciwgYW1iVmFyLCBhbGxTaGFycFZhciwgYWxsVG9uYWxWYXIsIGFsbEZsdWN0VmFyLCBhbGxSb3VnaFZhciwgYWxsSW1wdWxzVmFyKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpzZWVkcyA8LSBjKDQ5ODY1LCA3ODUyLCA4NDU5NjEsIDQxMDU4MywgMzY3NDgpDQoNCmBgYA0KDQojIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgbnRyZWVzPW50cmVlcywgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQpwDQoNCmBgYA0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDI1MDENCm10cnkgPC0gYXMuaW50ZWdlcihsZW5ndGgoaVZhcnMpLzEuNikNCg0KYGBgDQoNCiMjIyMjIyBSdW4gbW9kZWwNCg0KVHJhaW4gcHJlbGltaW5hcnkgbW9kZWwNCg0KYGBge3J9DQoNCm5wZXJtIDwtIDUNCg0KcmVzdWx0c091dFNRTXMyIDwtIGNyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sIG50cmVlPW50cmVlLCBtdHJ5PW10cnksIHBlcm1JbXBDb25kVGhyZXM9cGVybUltcENvbmRUaHJlcywgbnBlcm09bnBlcm0sIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KDQojIHByaW50IG1vZGVsIHByZWRpY3Rpb24gcmVzdWx0cw0KcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXN1bHRzT3V0U1FNczIkT09CX01BRQ0KcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQoNCmBgYA0KDQpUcmFpbiBtdWx0aXBsZSBzZWVkcyBtb2RlbA0KDQpgYGB7cn0NCg0KcmVzdWx0c091dFNRTXMyIDwtIG11bHRpX2NyZlJlZyhkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRTUU1zMiRPT0JfUk1TRQ0KcmVzdWx0c091dFNRTXMyJE9PQl9NQUUNCnJlc3VsdHNPdXRTUU1zMiRSc3F1YXJlZA0KDQpgYGANCg0KYGBge3J9DQoNCiMgc3RvcmUgcmVzdWx0cw0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIFNRTXMgbm8gdG9uYWwgbG91ZCcsICdSTVNFJ10gPC0gcmVzdWx0c091dFNRTXMyJE9PQl9STVNFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgU1FNcyBubyB0b25hbCBsb3VkJywgJ01BRSddIDwtIHJlc3VsdHNPdXRTUU1zMiRPT0JfTUFFDQpyZXNkSGlBbm5veUZpdEFCWydBbGwgU1FNcyBubyB0b25hbCBsb3VkJywgJ1JzcXVhcmVkJ10gPC0gcmVzdWx0c091dFNRTXMyJFJzcXVhcmVkDQpyZXNkSGlBbm5veVBlcm1JbXBBQiRBbGxTUU1zMiA8LSByZXN1bHRzT3V0U1FNczIkY29uZGl0aW9uYWxfcGVybWltcA0KDQpgYGANCg0KIyMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yLjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0U1FNczIuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFNRTXMyJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFNRTXMyLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFNRTXMyLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0U1FNczIuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1s3XSwgd2lkdGg9MC41KSArIGxhYnMoeD0iVmFyaWFibGUiLCB5PSJDb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlICglIGhpZ2hseSBhbm5veWVkKSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSkgKyBjb29yZF9mbGlwKHlsaW09YygwLCA0MCkpDQpwQmFyDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxTUU1zTm9Ub25MZENvblBlcm1pbXAuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTIuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxTUU1zTm9Ub25MZENvblBlcm1pbXAuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJkSGlBbm5veUFsbFNRTXNOb1RvbkxkQ29uUGVybWltcC5wZGYiLCB3aWR0aD04LCBoZWlnaHQ9Mi40LCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJkSGlBbm5veUFsbFNRTXNOb1RvbkxkQ29uUGVybWltcC5wZGYiKQ0KfQ0KDQpgYGANCg0KIyMjIyBkUHN5Y2hvYWNvdXN0aWMgYW5ub3lhbmNlIG1ldHJpY3MNCg0KIyMjIyMgU2V0IHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KaVZhcnMgPC0gYyhhbWJWYXIsICJkUHN5Y2hBbm5veVdpZG1hbm4iLCAiZFBzeWNoQW5ub3lNb3JlIiwgImRQc3ljaEFubm95RGkiLCAiZFBzeWNoQW5ub3lUb3JpamEiLCAiZFBzeWNoQW5ub3lXaWxsZW1zZW4iLCAiZFBzeWNoQW5ub3lCb3VjaGVyIiwgIlVBU1BzeWNoQW5ub3lXaWRtYW5uIiwgIlVBU1BzeWNoQW5ub3lNb3JlIiwgIlVBU1BzeWNoQW5ub3lEaSIsICJVQVNQc3ljaEFubm95VG9yaWphIiwgIlVBU1BzeWNoQW5ub3lXaWxsZW1zZW4iLCAiVUFTUHN5Y2hBbm5veUJvdWNoZXIiKQ0KZFZhciA8LSAiZEhpZ2hBbm5veVBjIg0KDQpzZWVkcyA8LSBjKDgzNTcwMiwgNTQsIDQ3MDkxMiwgNjUyLCA1NTI5NykNCg0KDQpgYGANCg0KIyMjIyMgSHlwZXJwYXJhbWV0ZXIgdHVuaW5nDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCg0KcCA8LSBtdHJ5VHVuZShkYXRhSW49c3RpbURhdGFOdW0sIGlWYXJzPWlWYXJzLCBkVmFyPWRWYXIsIHNlZWRzPXNlZWRzWzE6Ml0sDQogICAgICAgICAgICAgIG50cmVlcz1udHJlZXMsIG1pbnNwbGl0PW1pbnNwbGl0LCBtaW5idWNrZXQ9bWluYnVja2V0KQ0KcA0KDQpgYGANCg0KDQpTZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMNCg0KYGBge3J9DQoNCm50cmVlIDwtIDQwMDENCm10cnkgPC0gIGFzLmludGVnZXIobGVuZ3RoKGlWYXJzKS8xLjI1KQ0KDQpgYGANCg0KIyMjIyMgUnVuIG1vZGVsDQoNClRyYWluIHByZWxpbWluYXJ5IG1vZGVsDQoNCmBgYHtyfQ0KDQpucGVybSA8LSA1DQoNCnJlc3VsdHNPdXRQQSA8LSBjcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkc1sxOjJdLCBudHJlZT1udHJlZSwgbXRyeT1tdHJ5LCBwZXJtSW1wQ29uZFRocmVzPXBlcm1JbXBDb25kVGhyZXMsIG5wZXJtPW5wZXJtLCBtaW5zcGxpdD1taW5zcGxpdCwgbWluYnVja2V0PW1pbmJ1Y2tldCkNCg0KIyBwcmludCBtb2RlbCBwcmVkaWN0aW9uIHJlc3VsdHMNCnJlc3VsdHNPdXRQQSRPT0JfUk1TRQ0KcmVzdWx0c091dFBBJE9PQl9NQUUNCnJlc3VsdHNPdXRQQSRSc3F1YXJlZA0KDQpgYGANCg0KVHJhaW4gbXVsdGlwbGUgc2VlZHMgbW9kZWwNCg0KYGBge3J9DQoNCnJlc3VsdHNPdXRQQSA8LSBtdWx0aV9jcmZSZWcoZGF0YUluPXN0aW1EYXRhTnVtLCBpVmFycz1pVmFycywgZFZhcj1kVmFyLCBzZWVkcz1zZWVkcywgbnRyZWU9bnRyZWUsIG10cnk9bXRyeSwgcGVybUltcENvbmRUaHJlcz1wZXJtSW1wQ29uZFRocmVzLCBucGVybT1ucGVybSwgbWluc3BsaXQ9bWluc3BsaXQsIG1pbmJ1Y2tldD1taW5idWNrZXQpDQoNCiMgcHJpbnQgbW9kZWwgcHJlZGljdGlvbiByZXN1bHRzDQpyZXN1bHRzT3V0UEEkT09CX1JNU0UNCnJlc3VsdHNPdXRQQSRPT0JfTUFFDQpyZXN1bHRzT3V0UEEkUnNxdWFyZWQNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIHN0b3JlIHJlc3VsdHMNCnJlc2RIaUFubm95Rml0QUJbJ0FsbCBQc3ljaG9hY291c3RpYyBhbm5veWFuY2UnLCAnUk1TRSddIDwtIHJlc3VsdHNPdXRQQSRPT0JfUk1TRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIFBzeWNob2Fjb3VzdGljIGFubm95YW5jZScsICdNQUUnXSA8LSByZXN1bHRzT3V0UEEkT09CX01BRQ0KcmVzZEhpQW5ub3lGaXRBQlsnQWxsIFBzeWNob2Fjb3VzdGljIGFubm95YW5jZScsICdSc3F1YXJlZCddIDwtIHJlc3VsdHNPdXRQQSRSc3F1YXJlZA0KcmVzZEhpQW5ub3lQZXJtSW1wQUIkQWxsUEEgPC0gcmVzdWx0c091dFBBJGNvbmRpdGlvbmFsX3Blcm1pbXANCg0KYGBgDQoNCiMjIyMjIFBsb3QgcmVzdWx0cw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsZmlnLmhlaWdodD00LjR9DQpwYXIobWFpPWMoMCwzLDAsMCkpDQoNCiMgcGxvdCBjb25kaXRpb25hbCBpbXBvcnRhbmNlDQpyZXN1bHRzT3V0UEEuY29uaW1wIDwtIGFycmFuZ2UocmVzdWx0c091dFBBJGNvbmRpdGlvbmFsX3Blcm1pbXAsIGRlc2Mocm93X251bWJlcigpKSkNCg0KcEJhciA8LSBnZ3Bsb3QocmVzdWx0c091dFBBLmNvbmltcCkgKyBnZW9tX2NvbChhZXMoeD1mYWN0b3Iocm93bmFtZXMocmVzdWx0c091dFBBLmNvbmltcCksIGxldmVscz1yb3duYW1lcyhyZXN1bHRzT3V0UEEuY29uaW1wKSksIHk9Q29uZFBlcm1JbXApLCBmaWxsPW15Y29sb3Vyc1sxMF0sIHdpZHRoPTAuNSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iQ29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSAoJSBoaWdobHkgYW5ub3llZCkiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgNzApKQ0KcEJhcg0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmRIaUFubm95QWxsUEFDb25QZXJtaW1wLnN2ZyIsIHdpZHRoPTgsIGhlaWdodD00LjQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmRIaUFubm95QWxsUEFDb25QZXJtaW1wLnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCZEhpQW5ub3lBbGxQQUNvblBlcm1pbXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTQuNCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCZEhpQW5ub3lBbGxQQUNvblBlcm1pbXAucGRmIikNCn0NCg0KYGBgDQoNCiMjIyBTYXZlIHRoZSByZXN1bHRzIG91dHB1dHMgdG8gZmlsZQ0KDQpgYGB7cn0NCg0KaWYgKHNhdmVkYXRhKXsNCiAgdXRpbHM6OndyaXRlLmNzdihyZXNkSGlBbm5veUZpdEFCLCBwYXN0ZShvdXREYXRhUGF0aCwgIlxcUHRzQUJDUkZkSGlBbm5veU9PQkZpdC5jc3YiLCBzZXA9IiIpKQ0KICBpaSA8LSAwDQogIHRlbXAgPSBsaXN0KCkNCiAgZm9yIChyZXMgaW4gcmVzZEhpQW5ub3lQZXJtSW1wQUIpew0KICAgIGlpIDwtIGlpICsgMQ0KICAgIHRlbXBbW2lpXV0gPC0gYXMuZGF0YS5mcmFtZShyZXNkSGlBbm5veVBlcm1JbXBBQltpaV0pDQogICAgbmFtZXModGVtcFtbaWldXSkgPC0gbmFtZXMocmVzZEhpQW5ub3lQZXJtSW1wQUJbaWldKQ0KICB9DQogIG9wZW54bHN4Ojp3cml0ZS54bHN4KHRlbXAsIHBhc3RlKG91dERhdGFQYXRoLCAiXFxQdHNBQkNSRmRIaUFubm95Q29uUGVybWltcC54bHN4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgcm93TmFtZXM9VFJVRSkNCn0NCg0KYGBgDQoNCg0KIyMgUGFydHMgQSZCIHN1bW1hcnkNCg0KU3VtbWFyeSBvZiByZXN1bHRzIGZvciBQYXJ0cyBBICYgQiBjb21iaW5lZA0KDQojIyMgV2l0aCB0b25hbCBsb3VkbmVzcw0KDQojIyMjIEFic29sdXRlIHZhcmlhYmxlcw0KDQpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NH0NCiMgY29tYmluZSB0aGUgYW5ub3lhbmNlIHBlcm0gaW1wb3J0YW5jZSByZXN1bHRzDQoNCiMgY29udmVydCBlYWNoIHJlc3VsdCB0byBhIHRpYmJsZSB3aXRoIHJvd25hbWVzIGFkZGVkIHRvIGEgY29sdW1uLCByZW5hbWluZyB0aGUgZGF0YSBjb2x1bW4gdG8gJ2RBbm5veScgZXRjLg0KcmVzZEFubm95TW5BYnNQZXJtSW1wVGJsQUIgPC0gYXMuZGF0YS5mcmFtZShyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNTUU1zMS9tYXgocmVzZEFubm95TW5QZXJtSW1wQUIkQWJzU1FNczEpKSB8Pg0KICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXI9J1ZhcmlhYmxlJykNCmNvbG5hbWVzKHJlc2RBbm5veU1uQWJzUGVybUltcFRibEFCKVsyXSA8LSAiZEFubm95Ig0KDQpyZXNkSGlBbm5veUFic1Blcm1JbXBUYmxBQiA8LSBhcy5kYXRhLmZyYW1lKHJlc2RIaUFubm95UGVybUltcEFCJEFic1NRTXMxL21heChyZXNkSGlBbm5veVBlcm1JbXBBQiRBYnNTUU1zMSkpIHw+DQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhcj0nVmFyaWFibGUnKQ0KY29sbmFtZXMocmVzZEhpQW5ub3lBYnNQZXJtSW1wVGJsQUIpWzJdIDwtICJkSGlBbm5veSINCg0KIyBtZXJnZSB0aGUgZGF0YWZyYW1lcw0KcmVzQWJzUGVybUltcFRibEFCIDwtIGxpc3QocmVzZEFubm95TW5BYnNQZXJtSW1wVGJsQUIsIHJlc2RIaUFubm95QWJzUGVybUltcFRibEFCKSB8Pg0KICBwdXJycjo6cmVkdWNlKG1lcmdlLCBieSA9IGMoJ1ZhcmlhYmxlJyksIGFsbCA9IFQpDQoNCiMgcmVuYW1lIHRoZSBjb2x1bW5zDQpjb2xuYW1lcyhyZXNBYnNQZXJtSW1wVGJsQUIpWzI6M10gPC0gYygiTWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlIiwgIiVIQSB8IEhBJyAoYW1iLikiKQ0KcmVzQWJzUGVybUltcFRibEFCW2lzLm5hKHJlc0Fic1Blcm1JbXBUYmxBQildIDwtIDANCg0KcmVzQWJzQUIgPC0gdGlkeXI6OnBpdm90X2xvbmdlcihyZXNBYnNQZXJtSW1wVGJsQUIsIGNvbHM9LVZhcmlhYmxlLCBuYW1lc190bz0iT3V0Y29tZSIsIHZhbHVlc190bz0iSW1wIikNCg0KIyByZW9yZGVyIHJlcyB0aWJibGUsIGRlc2NlbmRpbmcgYnkgdGhlIHZhcmlhYmxlIEltcCBncm91cGVkIHN1bSBhbmQgY3JlYXRlIGNvbHVtbiB3aXRoIG5ldyBncm91cCBvcmRlciBhcyBhIGZhY3Rvcg0KcmVzQWJzQUIgPC0gcmVzQWJzQUIgfD4gbXV0YXRlKFZhcmlhYmxlX3N1bSA9IHN1bShJbXApLCAuYnk9VmFyaWFibGUpIHw+IGFycmFuZ2UoZGVzYyhWYXJpYWJsZV9zdW0pKSB8PiBncm91cF9ieShWYXJpYWJsZV9zdW0sIFZhcmlhYmxlKSB8Pg0KICAgbXV0YXRlKE9yZGVyID0gY3VyX2dyb3VwX2lkKCkpIHw+IG11dGF0ZShPcmRlciA9IGFzLmZhY3RvcihPcmRlcikpIHw+IGFycmFuZ2UoZGVzYyhPcmRlcikpDQoNCiMgUmVvcmRlciBvdXRjb21lIGxldmVscw0KcmVzQWJzQUIkT3V0Y29tZSA8LSBmYWN0b3IocmVzQWJzQUIkT3V0Y29tZSwgbGV2ZWxzPWMoIk1lYW4gY2hhbmdlIGluIGFubm95YW5jZSIsICIlSEEgfCBIQScgKGFtYi4pIikpDQoNCiMgcGxvdCByZXMgYXMgaG9yaXpvbnRhbCBiYXIgY2hhcnQsIHdpdGggSW1wIGFzIHkgYXhpcywgVmFyaWFibGUgYXMgeCBheGlzLCBPdXRjb21lIGFzIGZpbGwsIGFuZCBWYXJpYWJsZV9zdW0gYXMgb3JkZXIsIHJlbGFiZWwgeCBheGlzIHdpdGggVmFyaWFibGUgbmFtZXMNCnBCYXIgPC0gZ2dwbG90KHJlc0Fic0FCKSArIGdlb21fY29sKGFlcyhmaWxsPU91dGNvbWUsIHk9SW1wLCB4PU9yZGVyKSwgY29sb3VyPSdncmV5MzUnLCBsaW5ld2lkdGg9MCwgIHdpZHRoPTAuNzUsIHNob3cubGVnZW5kPVRSVUUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9Ik5vcm1hbGlzZWQgY29uZGl0aW9uYWwgdmFyaWFibGVcbnBlcm11dGF0aW9uIGltcG9ydGFuY2UiKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiksIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKGNvbG9yID0gcmdiKDIzNSwgMjM1LCAyMzUsIDEwMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksIGxpbmV3aWR0aCA9IDAuMjUsIGxpbmV0eXBlID0gMikpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgMSkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3VycywgbGFiZWxzPWMoZXhwcmVzc2lvbihwYXN0ZShiYXIoRGVsdGF+QSkpKSwgIiVIQSB8IEhBJyAoYW1iLikiKSkgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz11bmlxdWUocmV2KHJlc0Fic0FCJFZhcmlhYmxlKSkpICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSdPdXRjb21lJykpDQpwQmFyICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwgMSwgYnk9MC41KSkNCg0KaWYgKHNhdmVwbG90cyl7DQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJjcmZBYnNTUU1zU3VtbWFyeS5zdmciLCB3aWR0aD04LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCY3JmQWJzU1FNc1N1bW1hcnkuc3ZnIikNCg0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCY3JmQWJzU1FNc1N1bW1hcnkucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTQsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmNyZkFic1NRTXNTdW1tYXJ5LnBkZiIpDQp9DQoNCg0KIyBwbG90IHJlcyBhcyBob3Jpem9udGFsIGJhciBjaGFydCwgd2l0aCBJbXAgYXMgeSBheGlzLCBWYXJpYWJsZSBhcyB4IGF4aXMsIE91dGNvbWUgYXMgZmlsbCwgYW5kIFZhcmlhYmxlX3N1bSBhcyBvcmRlciwgcmVsYWJlbCB4IGF4aXMgd2l0aCBWYXJpYWJsZSBuYW1lcw0KcEJhciA8LSBnZ3Bsb3QocmVzQWJzQUIpICsgZ2VvbV9jb2woYWVzKGZpbGw9T3V0Y29tZSwgeT1JbXAsIHg9T3JkZXIpLCBjb2xvdXI9J2dyZXkzNScsIGxpbmV3aWR0aD0wLCAgd2lkdGg9MC43NSwgc2hvdy5sZWdlbmQ9VFJVRSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iTm9ybWFsaXNlZCBjb25kaXRpb25hbCB2YXJpYWJsZVxucGVybXV0YXRpb24gaW1wb3J0YW5jZSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSwgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgY29vcmRfZmxpcCh5bGltPWMoMCwgMSkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3VycywgbGFiZWxzPWMoZXhwcmVzc2lvbihwYXN0ZShiYXIoRGVsdGF+QSkpKSwgIiVIQSB8IEhBJyAoYW1iLikiKSkgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz11bmlxdWUocmV2KHJlc0Fic0FCJFZhcmlhYmxlKSkpICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSdPdXRjb21lJywgbnJvdz0yLCBuY29sPTEpKQ0KcEJhciArIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsIDEsIGJ5PTAuNSkpDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCY3JmQWJzU1FNc1N1bW1hcnlOdy5zdmciLCB3aWR0aD00LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCY3JmQWJzU1FNc1N1bW1hcnkuc3ZnIikNCg0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCY3JmQWJzU1FNc1N1bW1hcnlOdy5wZGYiLCB3aWR0aD00LCBoZWlnaHQ9NCwgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInBkZiIpKQ0KICB1bmxpbmsoIlB0c0FCY3JmQWJzU1FNc1N1bW1hcnkucGRmIikNCn0NCg0KYGBgDQoNCiMjIyMgQWxsIHZhcmlhYmxlcw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NH0NCiMgY29tYmluZSB0aGUgYW5ub3lhbmNlIHBlcm0gaW1wb3J0YW5jZSByZXN1bHRzDQoNCiMgY29udmVydCBlYWNoIHJlc3VsdCB0byBhIHRpYmJsZSB3aXRoIHJvd25hbWVzIGFkZGVkIHRvIGEgY29sdW1uLCByZW5hbWluZyB0aGUgZGF0YSBjb2x1bW4gdG8gJ2RBbm5veScgZXRjLg0KcmVzZEFubm95TW5BbGxQZXJtSW1wVGJsQUIgPC0gYXMuZGF0YS5mcmFtZShyZXNkQW5ub3lNblBlcm1JbXBBQiRBbGxTUU1zMS9tYXgocmVzZEFubm95TW5QZXJtSW1wQUIkQWxsU1FNczEpKSB8Pg0KICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXI9J1ZhcmlhYmxlJykNCmNvbG5hbWVzKHJlc2RBbm5veU1uQWxsUGVybUltcFRibEFCKVsyXSA8LSAiZEFubm95Ig0KDQpyZXNkSGlBbm5veUFsbFBlcm1JbXBUYmxBQiA8LSBhcy5kYXRhLmZyYW1lKHJlc2RIaUFubm95UGVybUltcEFCJEFsbFNRTXMxL21heChyZXNkSGlBbm5veVBlcm1JbXBBQiRBbGxTUU1zMSkpIHw+DQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhcj0nVmFyaWFibGUnKQ0KY29sbmFtZXMocmVzZEhpQW5ub3lBbGxQZXJtSW1wVGJsQUIpWzJdIDwtICJkSGlBbm5veSINCg0KIyBtZXJnZSB0aGUgZGF0YWZyYW1lcw0KcmVzQWxsUGVybUltcFRibEFCIDwtIGxpc3QocmVzZEFubm95TW5BbGxQZXJtSW1wVGJsQUIsIHJlc2RIaUFubm95QWxsUGVybUltcFRibEFCKSB8Pg0KICBwdXJycjo6cmVkdWNlKG1lcmdlLCBieSA9IGMoJ1ZhcmlhYmxlJyksIGFsbCA9IFQpDQoNCiMgcmVuYW1lIHRoZSBjb2x1bW5zDQpjb2xuYW1lcyhyZXNBbGxQZXJtSW1wVGJsQUIpWzI6M10gPC0gYygiTWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlIiwgIiVIQSB8IEhBJyAoYW1iLikiKQ0KcmVzQWxsUGVybUltcFRibEFCW2lzLm5hKHJlc0FsbFBlcm1JbXBUYmxBQildIDwtIDANCg0KcmVzQWxsQUIgPC0gdGlkeXI6OnBpdm90X2xvbmdlcihyZXNBbGxQZXJtSW1wVGJsQUIsIGNvbHM9LVZhcmlhYmxlLCBuYW1lc190bz0iT3V0Y29tZSIsIHZhbHVlc190bz0iSW1wIikNCg0KIyByZW9yZGVyIHJlcyB0aWJibGUsIGRlc2NlbmRpbmcgYnkgdGhlIHZhcmlhYmxlIEltcCBncm91cGVkIHN1bSBhbmQgY3JlYXRlIGNvbHVtbiB3aXRoIG5ldyBncm91cCBvcmRlciBhcyBhIGZhY3Rvcg0KcmVzQWxsQUIgPC0gcmVzQWxsQUIgfD4gbXV0YXRlKFZhcmlhYmxlX3N1bSA9IHN1bShJbXApLCAuYnk9VmFyaWFibGUpIHw+IGFycmFuZ2UoZGVzYyhWYXJpYWJsZV9zdW0pKSB8PiBncm91cF9ieShWYXJpYWJsZV9zdW0sIFZhcmlhYmxlKSB8Pg0KICAgbXV0YXRlKE9yZGVyID0gY3VyX2dyb3VwX2lkKCkpIHw+IG11dGF0ZShPcmRlciA9IGFzLmZhY3RvcihPcmRlcikpIHw+IGFycmFuZ2UoZGVzYyhPcmRlcikpDQoNCiMgUmVvcmRlciBvdXRjb21lIGxldmVscw0KcmVzQWxsQUIkT3V0Y29tZSA8LSBmYWN0b3IocmVzQWxsQUIkT3V0Y29tZSwgbGV2ZWxzPWMoIk1lYW4gY2hhbmdlIGluIGFubm95YW5jZSIsICIlSEEgfCBIQScgKGFtYi4pIikpDQoNCiMgcGxvdCByZXMgYXMgaG9yaXpvbnRhbCBiYXIgY2hhcnQsIHdpdGggSW1wIGFzIHkgYXhpcywgVmFyaWFibGUgYXMgeCBheGlzLCBPdXRjb21lIGFzIGZpbGwsIGFuZCBWYXJpYWJsZV9zdW0gYXMgb3JkZXIsIHJlbGFiZWwgeCBheGlzIHdpdGggVmFyaWFibGUgbmFtZXMNCnBCYXIgPC0gZ2dwbG90KHJlc0FsbEFCKSArIGdlb21fY29sKGFlcyhmaWxsPU91dGNvbWUsIHk9SW1wLCB4PU9yZGVyKSwgY29sb3VyPSdncmV5MzUnLCBsaW5ld2lkdGg9MCwgIHdpZHRoPTAuNzUsIHNob3cubGVnZW5kPVRSVUUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9Ik5vcm1hbGlzZWQgY29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyBjb29yZF9mbGlwKHlsaW09YygtMC4xLCAxLjEpKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teWNvbG91cnMpICsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9dW5pcXVlKHJldihyZXNBbGxBQiRWYXJpYWJsZSkpKQ0KcEJhciArIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsIDEsIGJ5PTAuNSkpDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCY3JmQWxsU1FNc1N1bW1hcnkuc3ZnIiwgd2lkdGg9OCwgaGVpZ2h0PTMsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJzdmciKSkNCiAgdW5saW5rKCJQdHNBQmNyZkFsbFNRTXNTdW1tYXJ5LnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCY3JmQWxsU1FNc1N1bW1hcnkucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTMsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmNyZkFsbFNRTXNTdW1tYXJ5LnBkZiIpDQp9DQoNCmBgYA0KDQojIyMgTm8gdG9uYWwgbG91ZG5lc3MNCg0KIyMjIyBBYnNvbHV0ZSB2YXJpYWJsZXMNCg0KYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTR9DQojIGNvbWJpbmUgdGhlIGFubm95YW5jZSBwZXJtIGltcG9ydGFuY2UgcmVzdWx0cw0KDQojIGNvbnZlcnQgZWFjaCByZXN1bHQgdG8gYSB0aWJibGUgd2l0aCByb3duYW1lcyBhZGRlZCB0byBhIGNvbHVtbiwgcmVuYW1pbmcgdGhlIGRhdGEgY29sdW1uIHRvICdkQW5ub3knIGV0Yy4NCnJlc2RBbm5veU1uQWJzUGVybUltcE5vVG9uTGRUYmxBQiA8LSBhcy5kYXRhLmZyYW1lKHJlc2RBbm5veU1uUGVybUltcEFCJEFic1NRTXMyL21heChyZXNkQW5ub3lNblBlcm1JbXBBQiRBYnNTUU1zMikpIHw+DQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhcj0nVmFyaWFibGUnKQ0KY29sbmFtZXMocmVzZEFubm95TW5BYnNQZXJtSW1wTm9Ub25MZFRibEFCKVsyXSA8LSAiZEFubm95Ig0KDQpyZXNkSGlBbm5veUFic1Blcm1JbXBOb1RvbkxkVGJsQUIgPC0gYXMuZGF0YS5mcmFtZShyZXNkSGlBbm5veVBlcm1JbXBBQiRBYnNTUU1zMi9tYXgocmVzZEhpQW5ub3lQZXJtSW1wQUIkQWJzU1FNczIpKSB8Pg0KICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXI9J1ZhcmlhYmxlJykNCmNvbG5hbWVzKHJlc2RIaUFubm95QWJzUGVybUltcE5vVG9uTGRUYmxBQilbMl0gPC0gImRIaUFubm95Ig0KDQojIG1lcmdlIHRoZSBkYXRhZnJhbWVzDQpyZXNBYnNQZXJtSW1wTm9Ub25MZFRibEFCIDwtIGxpc3QocmVzZEFubm95TW5BYnNQZXJtSW1wTm9Ub25MZFRibEFCLCByZXNkSGlBbm5veUFic1Blcm1JbXBOb1RvbkxkVGJsQUIpIHw+DQogIHB1cnJyOjpyZWR1Y2UobWVyZ2UsIGJ5ID0gYygnVmFyaWFibGUnKSwgYWxsID0gVCkNCg0KIyByZW5hbWUgdGhlIGNvbHVtbnMNCmNvbG5hbWVzKHJlc0Fic1Blcm1JbXBOb1RvbkxkVGJsQUIpWzI6M10gPC0gYygiTWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlIiwgIiVIQSB8IEhBJyAoYW1iLikiKQ0KcmVzQWJzUGVybUltcE5vVG9uTGRUYmxBQltpcy5uYShyZXNBYnNQZXJtSW1wTm9Ub25MZFRibEFCKV0gPC0gMA0KDQpyZXNBYnNOb1RvbkxkQUIgPC0gdGlkeXI6OnBpdm90X2xvbmdlcihyZXNBYnNQZXJtSW1wTm9Ub25MZFRibEFCLCBjb2xzPS1WYXJpYWJsZSwgbmFtZXNfdG89Ik91dGNvbWUiLCB2YWx1ZXNfdG89IkltcCIpDQoNCiMgcmVvcmRlciByZXMgdGliYmxlLCBkZXNjZW5kaW5nIGJ5IHRoZSB2YXJpYWJsZSBJbXAgZ3JvdXBlZCBzdW0gYW5kIGNyZWF0ZSBjb2x1bW4gd2l0aCBuZXcgZ3JvdXAgb3JkZXIgYXMgYSBmYWN0b3INCnJlc0Fic05vVG9uTGRBQiA8LSByZXNBYnNOb1RvbkxkQUIgfD4gbXV0YXRlKFZhcmlhYmxlX3N1bSA9IHN1bShJbXApLCAuYnk9VmFyaWFibGUpIHw+IGFycmFuZ2UoZGVzYyhWYXJpYWJsZV9zdW0pKSB8PiBncm91cF9ieShWYXJpYWJsZV9zdW0sIFZhcmlhYmxlKSB8Pg0KICAgbXV0YXRlKE9yZGVyID0gY3VyX2dyb3VwX2lkKCkpIHw+IG11dGF0ZShPcmRlciA9IGFzLmZhY3RvcihPcmRlcikpIHw+IGFycmFuZ2UoZGVzYyhPcmRlcikpDQoNCiMgUmVvcmRlciBvdXRjb21lIGxldmVscw0KcmVzQWJzTm9Ub25MZEFCJE91dGNvbWUgPC0gZmFjdG9yKHJlc0Fic05vVG9uTGRBQiRPdXRjb21lLCBsZXZlbHM9YygiTWVhbiBjaGFuZ2UgaW4gYW5ub3lhbmNlIiwgIiVIQSB8IEhBJyAoYW1iLikiKSkNCg0KIyBwbG90IHJlcyBhcyBob3Jpem9udGFsIGJhciBjaGFydCwgd2l0aCBJbXAgYXMgeSBheGlzLCBWYXJpYWJsZSBhcyB4IGF4aXMsIE91dGNvbWUgYXMgZmlsbCwgYW5kIFZhcmlhYmxlX3N1bSBhcyBvcmRlciwgcmVsYWJlbCB4IGF4aXMgd2l0aCBWYXJpYWJsZSBuYW1lcw0KcEJhciA8LSBnZ3Bsb3QocmVzQWJzTm9Ub25MZEFCKSArIGdlb21fY29sKGFlcyhmaWxsPU91dGNvbWUsIHk9SW1wLCB4PU9yZGVyKSwgY29sb3VyPSdncmV5MzUnLCBsaW5ld2lkdGg9MCwgIHdpZHRoPTAuNzUsIHNob3cubGVnZW5kPVRSVUUpICsgbGFicyh4PSJWYXJpYWJsZSIsIHk9Ik5vcm1hbGlzZWQgY29uZGl0aW9uYWwgdmFyaWFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSIpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiKSwgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoY29sb3IgPSByZ2IoMjM1LCAyMzUsIDIzNSwgMTAwLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgbGluZXdpZHRoID0gMC4yNSwgbGluZXR5cGUgPSAyKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyBjb29yZF9mbGlwKHlsaW09YygtMC4xLCAxLjEpKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teWNvbG91cnMpICsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9dW5pcXVlKHJldihyZXNBYnNOb1RvbkxkQUIkVmFyaWFibGUpKSkNCnBCYXIgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLCAxLCBieT0wLjUpKQ0KDQppZiAoc2F2ZXBsb3RzKXsNCiAgZ2dzYXZlKGZpbGVuYW1lPSJQdHNBQmNyZkFic1NRTXNOb1RvbkxkU3VtbWFyeS5zdmciLCB3aWR0aD04LCBoZWlnaHQ9MywgcGF0aD1maWxlLnBhdGgob3V0RmlnUGF0aCwgInN2ZyIpKQ0KICB1bmxpbmsoIlB0c0FCY3JmQWJzU1FNc05vVG9uTGRTdW1tYXJ5LnN2ZyIpDQogIA0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCY3JmQWJzU1FNc05vVG9uTGRTdW1tYXJ5LnBkZiIsIHdpZHRoPTgsIGhlaWdodD0zLCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAicGRmIikpDQogIHVubGluaygiUHRzQUJjcmZBYnNTUU1zTm9Ub25MZFN1bW1hcnkucGRmIikNCn0NCg0KYGBgDQoNCiMjIyMgQWxsIHZhcmlhYmxlcw0KDQpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NH0NCiMgY29tYmluZSB0aGUgYW5ub3lhbmNlIHBlcm0gaW1wb3J0YW5jZSByZXN1bHRzDQoNCiMgY29udmVydCBlYWNoIHJlc3VsdCB0byBhIHRpYmJsZSB3aXRoIHJvd25hbWVzIGFkZGVkIHRvIGEgY29sdW1uLCByZW5hbWluZyB0aGUgZGF0YSBjb2x1bW4gdG8gJ2RBbm5veScgZXRjLg0KcmVzZEFubm95TW5BbGxQZXJtSW1wTm9Ub25MZFRibEFCIDwtIGFzLmRhdGEuZnJhbWUocmVzZEFubm95TW5QZXJtSW1wQUIkQWxsU1FNczIvbWF4KHJlc2RBbm5veU1uUGVybUltcEFCJEFsbFNRTXMyKSkgfD4NCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyPSdWYXJpYWJsZScpDQpjb2xuYW1lcyhyZXNkQW5ub3lNbkFsbFBlcm1JbXBOb1RvbkxkVGJsQUIpWzJdIDwtICJkQW5ub3kiDQoNCnJlc2RIaUFubm95QWxsUGVybUltcE5vVG9uTGRUYmxBQiA8LSBhcy5kYXRhLmZyYW1lKHJlc2RIaUFubm95UGVybUltcEFCJEFsbFNRTXMyL21heChyZXNkSGlBbm5veVBlcm1JbXBBQiRBbGxTUU1zMikpIHw+DQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhcj0nVmFyaWFibGUnKQ0KY29sbmFtZXMocmVzZEhpQW5ub3lBbGxQZXJtSW1wTm9Ub25MZFRibEFCKVsyXSA8LSAiZEhpQW5ub3kiDQoNCiMgbWVyZ2UgdGhlIGRhdGFmcmFtZXMNCnJlc0FsbFBlcm1JbXBOb1RvbkxkVGJsQUIgPC0gbGlzdChyZXNkQW5ub3lNbkFsbFBlcm1JbXBOb1RvbkxkVGJsQUIsIHJlc2RIaUFubm95QWxsUGVybUltcE5vVG9uTGRUYmxBQikgfD4NCiAgcHVycnI6OnJlZHVjZShtZXJnZSwgYnkgPSBjKCdWYXJpYWJsZScpLCBhbGwgPSBUKQ0KDQojIHJlbmFtZSB0aGUgY29sdW1ucw0KY29sbmFtZXMocmVzQWxsUGVybUltcE5vVG9uTGRUYmxBQilbMjozXSA8LSBjKCJNZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UiLCAiJUhBIHwgSEEnIChhbWIuKSIpDQpyZXNBbGxQZXJtSW1wTm9Ub25MZFRibEFCW2lzLm5hKHJlc0FsbFBlcm1JbXBOb1RvbkxkVGJsQUIpXSA8LSAwDQoNCnJlc0FsbE5vVG9uTGRBQiA8LSB0aWR5cjo6cGl2b3RfbG9uZ2VyKHJlc0FsbFBlcm1JbXBOb1RvbkxkVGJsQUIsIGNvbHM9LVZhcmlhYmxlLCBuYW1lc190bz0iT3V0Y29tZSIsIHZhbHVlc190bz0iSW1wIikNCg0KIyByZW9yZGVyIHJlcyB0aWJibGUsIGRlc2NlbmRpbmcgYnkgdGhlIHZhcmlhYmxlIEltcCBncm91cGVkIHN1bSBhbmQgY3JlYXRlIGNvbHVtbiB3aXRoIG5ldyBncm91cCBvcmRlciBhcyBhIGZhY3Rvcg0KcmVzQWxsTm9Ub25MZEFCIDwtIHJlc0FsbE5vVG9uTGRBQiB8PiBtdXRhdGUoVmFyaWFibGVfc3VtID0gc3VtKEltcCksIC5ieT1WYXJpYWJsZSkgfD4gYXJyYW5nZShkZXNjKFZhcmlhYmxlX3N1bSkpIHw+IGdyb3VwX2J5KFZhcmlhYmxlX3N1bSwgVmFyaWFibGUpIHw+DQogICBtdXRhdGUoT3JkZXIgPSBjdXJfZ3JvdXBfaWQoKSkgfD4gbXV0YXRlKE9yZGVyID0gYXMuZmFjdG9yKE9yZGVyKSkgfD4gYXJyYW5nZShkZXNjKE9yZGVyKSkNCg0KIyBSZW9yZGVyIG91dGNvbWUgbGV2ZWxzDQpyZXNBbGxOb1RvbkxkQUIkT3V0Y29tZSA8LSBmYWN0b3IocmVzQWxsTm9Ub25MZEFCJE91dGNvbWUsIGxldmVscz1jKCJNZWFuIGNoYW5nZSBpbiBhbm5veWFuY2UiLCAiJUhBIHwgSEEnIChhbWIuKSIpKQ0KDQojIHBsb3QgcmVzIGFzIGhvcml6b250YWwgYmFyIGNoYXJ0LCB3aXRoIEltcCBhcyB5IGF4aXMsIFZhcmlhYmxlIGFzIHggYXhpcywgT3V0Y29tZSBhcyBmaWxsLCBhbmQgVmFyaWFibGVfc3VtIGFzIG9yZGVyLCByZWxhYmVsIHggYXhpcyB3aXRoIFZhcmlhYmxlIG5hbWVzDQpwQmFyIDwtIGdncGxvdChyZXNBbGxOb1RvbkxkQUIpICsgZ2VvbV9jb2woYWVzKGZpbGw9T3V0Y29tZSwgeT1JbXAsIHg9T3JkZXIpLCBjb2xvdXI9J2dyZXkzNScsIGxpbmV3aWR0aD0wLCAgd2lkdGg9MC43NSwgc2hvdy5sZWdlbmQ9VFJVRSkgKyBsYWJzKHg9IlZhcmlhYmxlIiwgeT0iTm9ybWFsaXNlZCBjb25kaXRpb25hbCB2YXJpYWJsZSBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlIikgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIpLCBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShjb2xvciA9IHJnYigyMzUsIDIzNSwgMjM1LCAxMDAsIG1heENvbG9yVmFsdWUgPSAyNTUpLCBsaW5ld2lkdGggPSAwLjI1LCBsaW5ldHlwZSA9IDIpLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArIGNvb3JkX2ZsaXAoeWxpbT1jKC0wLjEsIDEuMSkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3VycykgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz11bmlxdWUocmV2KHJlc0FsbE5vVG9uTGRBQiRWYXJpYWJsZSkpKQ0KcEJhciArIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsIDEsIGJ5PTAuNSkpDQoNCmlmIChzYXZlcGxvdHMpew0KICBnZ3NhdmUoZmlsZW5hbWU9IlB0c0FCY3JmQWxsU1FNc05vVG9uTGRTdW1tYXJ5LnN2ZyIsIHdpZHRoPTgsIGhlaWdodD0zLCBwYXRoPWZpbGUucGF0aChvdXRGaWdQYXRoLCAic3ZnIikpDQogIHVubGluaygiUHRzQUJjcmZBbGxTUU1zTm9Ub25MZFN1bW1hcnkuc3ZnIikNCiAgDQogIGdnc2F2ZShmaWxlbmFtZT0iUHRzQUJjcmZBbGxTUU1zTm9Ub25MZFN1bW1hcnkucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTMsIHBhdGg9ZmlsZS5wYXRoKG91dEZpZ1BhdGgsICJwZGYiKSkNCiAgdW5saW5rKCJQdHNBQmNyZkFsbFNRTXNOb1RvbkxkU3VtbWFyeS5wZGYiKQ0KfQ0KDQpgYGANCg0KIyMjIFNhdmUgdGhlIHJlc3VsdHMgb3V0cHV0cyB0byBmaWxlDQoNCmBgYHtyfQ0KIyBNYWtlIGEgbGlzdCBvZiB0aGUgc3VtbWFyeSByZXN1bHRzDQpyZXNTdW1tYXJ5IDwtIGxpc3QocmVzQWJzQUIsIHJlc0FsbEFCLCByZXNBYnNOb1RvbkxkQUIsIHJlc0FsbE5vVG9uTGRBQikNCg0KIyBTYXZlIHRoZSByZXN1bHRzDQppZiAoc2F2ZWRhdGEpew0KICBpaSA8LSAwDQogIHRlbXAgPSBsaXN0KCkNCiAgZm9yIChyZXMgaW4gcmVzU3VtbWFyeSl7DQogICAgaWkgPC0gaWkgKyAxDQogICAgdGVtcFtbaWldXSA8LSBkYXRhLmZyYW1lKHJlc1N1bW1hcnlbaWldKQ0KICB9DQogIG9wZW54bHN4Ojp3cml0ZS54bHN4KHRlbXAsIHBhc3RlKG91dERhdGFQYXRoLCAiXFxQdHNBQkNSRlN1bW1hcnkueGxzeCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iIiksDQogICAgICAgICAgICAgICAgICAgICAgIHJvd05hbWVzPVRSVUUpDQp9DQoNCmBgYA==